diff --git a/.bowerrc b/.bowerrc deleted file mode 100755 index 0eed5ab6..00000000 --- a/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory" : "bower_components" -} diff --git a/.editorconfig b/.editorconfig index 0ea0cc46..d1d8a417 100755 --- a/.editorconfig +++ b/.editorconfig @@ -3,7 +3,7 @@ root = true [*] indent_style = space -indent_size = 4 +indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore index be41f7e2..7930e80f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -bower_components node_modules +yarn-error.log +.yarnclean temp _SpecRunner.html .DS_Store diff --git a/Gruntfile.js b/Gruntfile.js index f9f5f8e3..45434d39 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -19,16 +19,16 @@ 'use strict'; var pkg = require('./package.json'); -var AV_CONFIG_VERSION = '17.04'; +var AV_CONFIG_VERSION = '20.2.0'; //Using exclusion patterns slows down Grunt significantly //instead of creating a set of patterns like '**/*.js' and '!**/node_modules/**' //this method is used to create a set of inclusive patterns for all subdirectories -//skipping node_modules, bower_components, dist, and any .dirs +//skipping node_modules, dist, and any .dirs //This enables users to create any directory structure they desire. var createFolderGlobs = function(fileTypePatterns) { fileTypePatterns = Array.isArray(fileTypePatterns) ? fileTypePatterns : [fileTypePatterns]; - var ignore = ['node_modules','bower_components','dist','temp']; + var ignore = ['node_modules','dist','temp']; var fs = require('fs'); return fs.readdirSync(process.cwd()) .map(function(file){ @@ -118,12 +118,13 @@ module.exports = function (grunt) { less: { production: { options: { + strictImports: true }, files: [{ expand: true, src: ['themes/**/app.less'], dest: 'temp/', - ext: '.css', + ext: '.css' }] } }, @@ -145,37 +146,6 @@ module.exports = function (grunt) { dest: 'temp/templates.js' } }, - copy: { - main: { - files: [ - {src: ['img/**'], dest: 'dist/'}, - {src: ['temp_data/**'], dest: 'dist/'}, - {src: ['avUi/**/*.less'], dest: 'dist/less/'}, - {src: ['themes/**'], dest: 'dist/'}, - { - expand: true, - cwd: 'bower_components/bootstrap/fonts/', - src: ['**'], - dest: 'dist/themes/fonts/' - }, - { - expand: true, - cwd: 'bower_components/bootstrap/fonts/', - src: ['**'], - dest: 'dist/themes/fonts/' - }, - { - expand: true, - cwd: 'bower_components/font-awesome/fonts/', - src: ['**'], - dest: 'dist/fonts/' - } - //{src: ['bower_components/angular-ui-utils/ui-utils-ieshiv.min.js'], dest: 'dist/'}, - //{src: ['bower_components/select2/*.png','bower_components/select2/*.gif'], dest:'dist/css/',flatten:true,expand:true}, - //{src: ['bower_components/angular-mocks/angular-mocks.js'], dest: 'dist/'} - ] - } - }, dom_munger:{ read: { options: { @@ -192,10 +162,10 @@ module.exports = function (grunt) { options: { remove: ['script[data-remove!="false"]','link[data-remove!="false"]'], append: [ - {selector:'body',html:''}, + {selector:'body',html:''}, {selector:'body',html:''}, - {selector:'body',html:''}, - {selector:'body',html:''}, + {selector:'body',html:''}, + {selector:'body',html:''}, {selector:'head',html:''} ] }, @@ -225,9 +195,9 @@ module.exports = function (grunt) { 'temp/libnocompat.js': ['<%= dom_munger.data.libnocompatjs %>'], 'temp/lib.js': ['<%= dom_munger.data.libjs %>'], 'temp/app.js': ['<%= dom_munger.data.appjs %>','<%= ngtemplates.main.dest %>'], - 'dist/avConfig-v17.04.js': ['avConfig.js'], - 'dist/avThemes-v17.04.js': ['avThemes.js'], - 'dist/avPlugins-v17.04.js': ['plugins/**/*.js'] + 'dist/avConfig-v20.2.0.js': ['avConfig.js'], + 'dist/avThemes-v20.2.0.js': ['avThemes.js'], + 'dist/avPlugins-v20.2.0.js': ['plugins/**/*.js'] } } }, @@ -237,6 +207,8 @@ module.exports = function (grunt) { "dist/locales/en.json": ["locales/en.json", "plugins/**/locales/en.json"], "dist/locales/es.json": ["locales/es.json", "plugins/**/locales/es.json"], "dist/locales/gl.json": ["locales/gl.json", "plugins/**/locales/gl.json"], + "dist/locales/sv.json": ["locales/sv.json", "plugins/**/locales/sv.json"], + "dist/locales/fi.json": ["locales/fi.json", "plugins/**/locales/fi.json"], "dist/locales/ca.json": ["locales/ca.json", "plugins/**/locales/ca.json"] } } @@ -259,18 +231,69 @@ module.exports = function (grunt) { beautify: true }, files: { - 'dist/appCommon-v17.04.js': 'temp/app.js', - 'dist/libCommon-v17.04.js': 'temp/lib.js', - 'dist/libnocompat-v17.04.js': 'temp/libnocompat.js', - 'dist/libcompat-v17.04.js': 'temp/libcompat.js', + 'dist/appCommon-v20.2.0.js': 'temp/app.js', + 'dist/libCommon-v20.2.0.js': 'temp/lib.js', + 'dist/libnocompat-v20.2.0.js': 'temp/libnocompat.js', + 'dist/libcompat-v20.2.0.js': 'temp/libcompat.js', 'dist/avWidgets.js': 'avWidgets.js', - "dist/locales/moment/es.js": "bower_components/moment/lang/es.js", - "dist/locales/moment/gl.js": "bower_components/moment/lang/gl.js", - "dist/locales/moment/ca.js": "bower_components/moment/lang/ca.js" + "dist/locales/moment/es.js": "node_modules/moment/locale/es.js", + "dist/locales/moment/gl.js": "node_modules/moment/locale/gl.js", + "dist/locales/moment/ca.js": "node_modules/moment/locale/ca.js" } } }, + copy: { + temp: { + files: [ + { + expand: true, + cwd: 'node_modules/nanoscroller/bin/css/', + src: ['*'], + dest: 'temp/' + }, + { + expand: true, + cwd: 'node_modules/intl-tel-input/build/css/', + src: ['*'], + dest: 'temp/' + } + ] + }, + main: { + files: [ + {src: ['img/**'], dest: 'dist/'}, + {src: ['node_modules/intl-tel-input/build/img/flags.png'], dest: 'dist/img/flags.png'}, + {src: ['node_modules/intl-tel-input/build/js/utils.js'], dest: 'dist/utils.js'}, + {src: ['node_modules/intl-tel-input/build/css/intlTelInput.css'], dest: 'dist/intlTelInput.css'}, + {src: ['node_modules/nanoscroller/bin/css/*.css'], dest: 'temp/'}, + {src: ['temp_data/**'], dest: 'dist/'}, + {src: ['avUi/**/*.less'], dest: 'dist/less/'}, + {src: ['themes/**'], dest: 'dist/'}, + { + expand: true, + cwd: 'node_modules/bootstrap/fonts/', + src: ['**'], + dest: 'dist/themes/fonts/' + }, + { + expand: true, + cwd: 'node_modules/bootstrap/fonts/', + src: ['**'], + dest: 'dist/themes/fonts/' + }, + { + expand: true, + cwd: 'node_modules/font-awesome/fonts/', + src: ['**'], + dest: 'dist/fonts/' + } + //{src: ['node_modules/angular-ui-utils/ui-utils-ieshiv.min.js'], dest: 'dist/'}, + //{src: ['node_modules/select2/*.png','node_modules/select2/*.gif'], dest:'dist/css/',flatten:true,expand:true}, + //{src: ['node_modules/angular-mocks/angular-mocks.js'], dest: 'dist/'} + ] + } + }, htmlmin: { main: { options: { @@ -307,7 +330,7 @@ module.exports = function (grunt) { 'avWidgets.js', '<%= dom_munger.data.appjs %>', '<%= ngtemplates.main.dest %>', - 'bower_components/angular-mocks/angular-mocks.js', + 'node_modules/angular-mocks/angular-mocks.js', createFolderGlobs('*-spec.js') ], logLevel:'ERROR', @@ -329,20 +352,40 @@ module.exports = function (grunt) { noColor: false, // If true, protractor will not use colors in its output. args: { // Arguments passed to the command - } - }, - //your_target: { // Grunt requires at least one target to run so you can simply put 'all: {}' here too. - all: { - options: { - configFile: "e2e.conf.js", // Target-specific config file - args: {} // Target-specific arguments - } - }, - }, - + } + }, + //your_target: { // Grunt requires at least one target to run so you can simply put 'all: {}' here too. + all: { + options: { + configFile: "e2e.conf.js", // Target-specific config file + args: {} // Target-specific arguments + } + }, + } }); - grunt.registerTask('build',['check_config', 'jshint','clean:before','less','autoprefixer','dom_munger','ngtemplates','cssmin','concat','merge-json','ngAnnotate','uglify','copy','htmlmin','imagemin','clean:after']); + grunt.registerTask( + 'build', + [ + 'check_config', + 'jshint', + 'clean:before', + 'copy:temp', + 'less', + 'autoprefixer', + 'dom_munger', + 'ngtemplates', + 'cssmin', + 'concat', + 'merge-json', + 'ngAnnotate', + 'uglify', + 'copy:main', + 'htmlmin', + 'imagemin', + 'clean:after' + ] + ); grunt.registerTask('serve', ['dom_munger:read','jshint','connect', 'watch']); grunt.registerTask('test',['dom_munger:read','karma:all_tests']); @@ -368,7 +411,7 @@ module.exports = function (grunt) { if (grunt.file.exists(spec)) { var files = [].concat(grunt.config('dom_munger.data.libnocompatjs')); files.concat(grunt.config('dom_munger.data.libjs')); - files.push('bower_components/angular-mocks/angular-mocks.js'); + files.push('node_modules/angular-mocks/angular-mocks.js'); files.push('avConfig.js'); files.push('avThemes.js'); files.push('avWidgets.js'); diff --git a/README.md b/README.md index 8aded522..8b3bb6ca 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,13 @@ example) or install it from the web and follow the instructions in the README ./configure && make && sudo make install sudo chown -R `whoami` ~/.npm -You need also to install globally grunt and bower: +You need also to install globally grunt and yarn: - sudo npm install -g grunt-cli grunt bower + sudo npm install -g grunt-cli grunt yarn After that, you can install the agora-core-view javascript dependencies: - npm install && bower install + yarn Once that's done, you have 3 simple Grunt commands available: diff --git a/app.js b/app.js index 7fd2ab5d..a99c3e2a 100755 --- a/app.js +++ b/app.js @@ -83,6 +83,22 @@ angular.module('agora-gui-common').run(function($http, $rootScope) { }); }); +/* +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(); + } + } + }); + }; +}); /* This directive allows us to pass a function in on an enter key to do what we want. diff --git a/app.less b/app.less index 5514e164..67af4164 100755 --- a/app.less +++ b/app.less @@ -22,6 +22,7 @@ /* avRegistration */ @import "avRegistration/login-controller/login-controller.less"; @import "avRegistration/login-directive/login-directive.less"; +@import "avRegistration/openid-connect-directive/openid-connect-directive.less"; @import "avRegistration/register-directive/register-directive.less"; @import "avRegistration/field-directive/field-directive.less"; @import "avRegistration/fields/email-field-directive/email-field-directive.less"; @@ -30,13 +31,17 @@ @import "avRegistration/fields/dni-field-directive/dni-field-directive.less"; @import "avRegistration/fields/tel-field-directive/tel-field-directive.less"; @import "avRegistration/fields/code-field-directive/code-field-directive.less"; +@import "avRegistration/fields/date-field-directive/date-field-directive.less"; @import "avRegistration/fields/bool-field-directive/bool-field-directive.less"; @import "avRegistration/fields/image-field-directive/image-field-directive.less"; +@import "../../intlTelInput.css"; + /* avUi */ @import "avUi/simple-error-directive/simple-error-directive.less"; @import "avUi/change-lang-directive/change-lang-directive.less"; @import "avUi/foot-directive/foot-directive.less"; +@import "avUi/documentation-directive/documentation-directive.less"; /* Add Component LESS Above */ @import "../../app.less"; diff --git a/avConfig.js b/avConfig.js index e6a410e4..5ba8e1b0 100644 --- a/avConfig.js +++ b/avConfig.js @@ -20,7 +20,7 @@ * in this same file, which you might want to edit and tune if needed. */ -var AV_CONFIG_VERSION = '17.04'; +var AV_CONFIG_VERSION = '20.2.0'; var avConfigData = { // the base url path for ajax requests, for example for sending ballots or @@ -37,6 +37,9 @@ var avConfigData = { // Show 'Success Action' tab in admin agora_gui showSuccessAction: false, + // Configurable Sign Up link + signupLink: "/admin/signup", + // AuthApi base url authAPI: "https://agora/authapi/api/", dnieUrl: "https://agora.dev/authapi/api/authmethod/dnie/auth/", @@ -136,6 +139,9 @@ var avConfigData = { // if we are in debug mode or not debug: true, + // Information regarding OpenID Connect authentication + openIDConnectProviders: [], + // contact data where users can reach to a human when they need it contact: { // Support contact email displayed in the footer links diff --git a/avRegistration/auth-method-service.js b/avRegistration/auth-method-service.js index d822bfdd..83e0d665 100644 --- a/avRegistration/auth-method-service.js +++ b/avRegistration/auth-method-service.js @@ -17,7 +17,7 @@ angular.module('avRegistration') - .factory('Authmethod', function($http, $cookies, ConfigService, $interval) { + .factory('Authmethod', function($http, $cookies, ConfigService, $interval, $location) { var backendUrl = ConfigService.authAPI; var authId = ConfigService.freeAuthId; var authmethod = {}; @@ -26,6 +26,25 @@ angular.module('avRegistration') 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; }; @@ -40,13 +59,170 @@ angular.module('avRegistration') 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; + } + 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); + }; + + /** + * @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); + }; + + + /** + * @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); + }; + + /** + * @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, {}); + }; + + /** + * @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, {}); + }; + + 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; + } + return $http.post(backendUrl + 'user/extra/', extra); + }; + authmethod.getUserInfo = function(userid) { if (!authmethod.isLoggedIn()) { var data = { - success: function () { return data; }, - error: function (func) { + then: function (onSuccess, onError) { setTimeout(function() { - func({message:"not-logged-in"}); + onError({data: {message:"not-logged-in"}}); }, 0); return data; } @@ -63,10 +239,9 @@ angular.module('avRegistration') authmethod.ping = function() { if (!authmethod.isLoggedIn()) { var data = { - success: function () { return data; }, - error: function (func) { + then: function (onSuccess, onError) { setTimeout(function() { - func({message:"not-logged-in"}); + onError({data: {message:"not-logged-in"}}); }, 0); return data; } @@ -86,6 +261,12 @@ angular.module('avRegistration') 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); }; @@ -137,7 +318,14 @@ angular.module('avRegistration') }; authmethod.getRegisterFields = function (viewEventData) { - var fields = angular.copy(viewEventData.extra_fields); + var fields = _.filter( + angular.copy(viewEventData.extra_fields), + function (item) { + if (true === item.required_when_registered) { + return false; + } + return true; + }); if (!fields) { fields = []; } var found = false; @@ -152,14 +340,14 @@ angular.module('avRegistration') } }); - if (viewEventData.auth_method === "sms" && !found) { + if ((viewEventData.auth_method === "sms" || viewEventData.auth_method === "sms-otp") && !found) { fields.push({ "name": "tlf", "type": "tlf", "required": true, "required_on_authentication": true }); - } else if (viewEventData.auth_method === "email" && !found) { + } else if (_.contains(["email", 'email-otp'], viewEventData.auth_method) && !found) { fields.push({ "name": "email", "type": "email", @@ -167,6 +355,19 @@ angular.module('avRegistration') "required_on_authentication": true }); } else if (viewEventData.auth_method === "user-and-password") { + fields.push({ + "name": "username", + "type": "text", + "required": true, + "required_on_authentication": true + }); + fields.push({ + "name": "password", + "type": "password", + "required": true, + "required_on_authentication": true + }); + } else if (viewEventData.auth_method === "email-and-password") { fields.push({ "name": "email", "type": "email", @@ -181,7 +382,7 @@ angular.module('avRegistration') }); } - // put captha the last + // put captcha the last for (var i=0; i
+
diff --git a/avRegistration/fields/bool-field-directive/bool-field-directive.html b/avRegistration/fields/bool-field-directive/bool-field-directive.html index 52ddced2..84d6fe17 100644 --- a/avRegistration/fields/bool-field-directive/bool-field-directive.html +++ b/avRegistration/fields/bool-field-directive/bool-field-directive.html @@ -3,6 +3,7 @@ - +

- + + + +

+
diff --git a/avRegistration/fields/date-field-directive/date-field-directive.js b/avRegistration/fields/date-field-directive/date-field-directive.js new file mode 100644 index 00000000..b3ba3de5 --- /dev/null +++ b/avRegistration/fields/date-field-directive/date-field-directive.js @@ -0,0 +1,65 @@ +/** + * 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; + + var now = new Date(); + scope.date = { + year: now.getFullYear(), + month: now.getMonth() + 1, + day: now.getDate() + }; + + var initY = (new Date()).getFullYear(); + var i = 0; + + for (i=initY; i>=initY-130; i--) { + scope.years.push(i); + } + + for (i=1; i<=12; i++) { + scope.months.push(i); + } + + scope.getDays = function() { + var days = []; + var ndays = (new Date(scope.date.year, scope.date.month, 0)).getDate(); + for (i=1; i<=ndays; i++) { + days.push(i); + } + return days; + }; + + scope.onChange = function() { + scope.ngModel = scope.date.year + "-" + scope.date.month + "-" + scope.date.day; + }; + } + return { + restrict: 'AE', + link: link, + scope: { + ngModel: '=', + label: '=', + }, + templateUrl: 'avRegistration/fields/date-field-directive/date-field-directive.html' + }; + }); diff --git a/avRegistration/fields/date-field-directive/date-field-directive.less b/avRegistration/fields/date-field-directive/date-field-directive.less new file mode 100644 index 00000000..4968d898 --- /dev/null +++ b/avRegistration/fields/date-field-directive/date-field-directive.less @@ -0,0 +1,17 @@ +[avr-date-field] { + select:focus { + outline: 1px solid blue; + } + + .form-group { + min-height: 80px; + + .col-sm-8 { + padding-top: 8px; + + select { + margin-right: 5px; + } + } + } +} diff --git a/avRegistration/fields/dni-field-directive/dni-field-directive.html b/avRegistration/fields/dni-field-directive/dni-field-directive.html index 480275c5..6af491fb 100644 --- a/avRegistration/fields/dni-field-directive/dni-field-directive.html +++ b/avRegistration/fields/dni-field-directive/dni-field-directive.html @@ -2,13 +2,14 @@
-
diff --git a/avRegistration/fields/tel-field-directive/tel-field-directive.js b/avRegistration/fields/tel-field-directive/tel-field-directive.js index c95514d7..53ba0fe7 100644 --- a/avRegistration/fields/tel-field-directive/tel-field-directive.js +++ b/avRegistration/fields/tel-field-directive/tel-field-directive.js @@ -16,9 +16,74 @@ **/ angular.module('avRegistration') - .directive('avrTelField', function($state) { + .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); + } + } + }); + 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', @@ -26,4 +91,4 @@ angular.module('avRegistration') link: link, templateUrl: 'avRegistration/fields/tel-field-directive/tel-field-directive.html' }; - }); \ No newline at end of file + }); diff --git a/avRegistration/fields/tel-field-directive/tel-field-directive.less b/avRegistration/fields/tel-field-directive/tel-field-directive.less index 64bc9d93..31d07ab0 100644 --- a/avRegistration/fields/tel-field-directive/tel-field-directive.less +++ b/avRegistration/fields/tel-field-directive/tel-field-directive.less @@ -6,4 +6,62 @@ .code-help span { transition: .4s all; } +} + +.iti-flag { + background-image: url("../../img/flags.png") !important; +} + +.phone-login { + width: 100%; + height: 34px; + border: 1px solid; + border-color: #ccc; + border-radius: 4px; +} + +.phone-login.error { + border-color: #ff0000 !important; +} + +.input-error > .error { + color: #b70303; +} + +.intl-tel-input.allow-dropdown +{ + margin-top: 12px !important; + margin-bottom: -18px !important; + width: 100%; +} +.intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-2 .selected-flag +{ + width: 90px; +} +.intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-3 .selected-flag +{ + width: 98px; +} +.intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-4 .selected-flag +{ + width: 108px; +} +.intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-5 .selected-flag +{ + width: 120px; +} +.intl-tel-input .country-list { + max-height: 145px; + max-width: 420px; +} +.intl-tel-input .selected-flag .iti-arrow { + position: absolute; + top: 50%; + margin-top: -2px; + right: 6px; + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 8px solid #555; } \ No newline at end of file diff --git a/avRegistration/fields/text-field-directive/text-field-directive.html b/avRegistration/fields/text-field-directive/text-field-directive.html index 5205250c..6ea78a61 100644 --- a/avRegistration/fields/text-field-directive/text-field-directive.html +++ b/avRegistration/fields/text-field-directive/text-field-directive.html @@ -2,13 +2,16 @@
-
- \ No newline at end of file + diff --git a/avRegistration/fields/textarea-field-directive/textarea-field-directive.html b/avRegistration/fields/textarea-field-directive/textarea-field-directive.html index b84948de..ef1341b1 100644 --- a/avRegistration/fields/textarea-field-directive/textarea-field-directive.html +++ b/avRegistration/fields/textarea-field-directive/textarea-field-directive.html @@ -1,6 +1,6 @@
- +

{{field.help}}

diff --git a/avRegistration/login-controller/login-controller.html b/avRegistration/login-controller/login-controller.html index a940a654..5c53d3b2 100644 --- a/avRegistration/login-controller/login-controller.html +++ b/avRegistration/login-controller/login-controller.html @@ -1,6 +1,15 @@
-
+
+
+
-
\ No newline at end of file +
diff --git a/avRegistration/login-controller/login-controller.js b/avRegistration/login-controller/login-controller.js index d3a830f8..1eddd6fa 100644 --- a/avRegistration/login-controller/login-controller.js +++ b/avRegistration/login-controller/login-controller.js @@ -15,10 +15,22 @@ * along with agora-gui-common. If not, see . **/ -angular.module('avRegistration').controller('LoginController', - function($scope, $stateParams, $filter, ConfigService, $i18next) { - $scope.event_id = $stateParams.id; - $scope.code = $stateParams.code; - $scope.email = $stateParams.email; - } -); +angular.module('avRegistration') + .controller( + 'LoginController', + 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; + } + ); diff --git a/avRegistration/login-directive/login-directive.html b/avRegistration/login-directive/login-directive.html index abb3896e..51a93979 100644 --- a/avRegistration/login-directive/login-directive.html +++ b/avRegistration/login-directive/login-directive.html @@ -1,16 +1,34 @@
-

+

+

+

+

+

+

-
+
-
diff --git a/avRegistration/login-directive/login-directive.js b/avRegistration/login-directive/login-directive.js index 5d1a37b1..6cf2801d 100644 --- a/avRegistration/login-directive/login-directive.js +++ b/avRegistration/login-directive/login-directive.js @@ -16,31 +16,47 @@ **/ angular.module('avRegistration') - .directive('avLogin', function(Authmethod, - StateDataService, - $parse, - $state, - $cookies, - $i18next, - $window, - $timeout, - ConfigService) { - // we use it as something similar to a controller here - function link(scope, element, attrs) { + .directive( + 'avLogin', + function( + Authmethod, + StateDataService, + $parse, + $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; var adminId = ConfigService.freeAuthId + ''; - var autheventid = attrs.eventId; + 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 - if ($cookies.authevent && $cookies.authevent === adminId && - autheventid === adminId) + if (!!$cookies["authevent_" + adminId] && $cookies["authevent_" + adminId] === adminId && + autheventid === adminId && !!$cookies["auth_authevent_" + adminId]) { $window.location.href = '/admin/elections'; } 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; @@ -55,40 +71,116 @@ angular.module('avRegistration') 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); + } + scope.resendAuthCode = function(field) { - if (scope.sendingData || scope.method !== "sms") { + if (scope.sendingData || !_.contains(["email", "email-otp", "sms", "sms-otp"], scope.method)) { return; } + var data = {}; - if (scope.telIndex === -1) { - return; - } + // sms or sms-otp + if (_.contains(["sms", "sms-otp"], scope.method)) { + + if (scope.telIndex === -1) { + return; + } + + if (!isValidTel("input" + scope.telIndex)) { + return; + } + + data['tlf'] = scope.telField.value; + + // email or email-otp + } else if (_.contains(["email", "email-otp"], scope.method)) { + if (-1 === scope.emailIndex) { + return; + } + var email = scope.email; + if (null === email) { + email = scope.login_fields[scope.emailIndex].value; + } + if (!isValidEmail(email)) { + return; + } - if (scope.form["input" + scope.telIndex].$invalid) { - return; + data['email'] = email; } // reset code field, as we are going to send a new one - field.value = ""; - - var data = {}; - data['tlf'] = scope.telField.value; + if (!!field) { + field.value = ""; + } scope.sendingData = true; Authmethod.resendAuthCode(data, autheventid) - .success(function(rcvData) { - $timeout(scope.sendingDataTimeout, 3000); - }) - .error(function(error) { - $timeout(scope.sendingDataTimeout, 3000); - scope.error = $i18next('avRegistration.errorSendingAuthCode'); - }); + .then( + function(response) { + if (_.contains(["sms", "sms-otp"], scope.method)) { + scope.telField.disabled = true; + + // email or email-otp + } else { + scope.login_fields[scope.emailIndex].disabled = true; + } + scope.currentFormStep = 1; + $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; @@ -96,6 +188,12 @@ angular.module('avRegistration') 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, }; @@ -109,61 +207,89 @@ angular.module('avRegistration') }); scope.sendingData = true; - Authmethod.login(data, autheventid) - .success(function(rcvData) { - if (rcvData.status === "ok") { - scope.khmac = rcvData.khmac; - $cookies.authevent = autheventid; - $cookies.userid = rcvData.username; - $cookies.user = scope.email; - $cookies.auth = rcvData['auth-token']; - $cookies.isAdmin = scope.isAdmin; - Authmethod.setAuth($cookies.auth, scope.isAdmin); - if (scope.isAdmin) - { - Authmethod.getUserInfo().success(function(d) { - $cookies.user = d.email; + Authmethod + .login(data, autheventid) + .then( + function onSuccess(response) { + if (response.data.status === "ok") { + scope.khmac = response.data.khmac; + var postfix = "_authevent_" + autheventid; + $cookies["authevent_" + autheventid] = autheventid; + $cookies["userid" + postfix] = response.data.username; + $cookies["user" + postfix] = scope.email; + $cookies["auth" + postfix] = response.data['auth-token']; + $cookies["isAdmin" + postfix] = scope.isAdmin; + Authmethod.setAuth($cookies["auth" + postfix], scope.isAdmin, autheventid); + if (scope.isAdmin) + { + Authmethod.getUserInfo() + .then( + function onSuccess(response) { + $cookies["user" + postfix] = response.data.email; $window.location.href = '/admin/elections'; - }).error(function(error) { + }, + function onError(response) { $window.location.href = '/admin/elections'; - }); - } - else if (angular.isDefined(rcvData['redirect-to-url'])) - { - $window.location.href = rcvData['redirect-to-url']; - } - else - { - // redirecting to vote link - Authmethod.getPerm("vote", "AuthEvent", autheventid) - .success(function(rcvData2) { - var khmac = rcvData2['permission-token']; - var path = khmac.split(";")[1]; - var hash = path.split("/")[0]; - var msg = path.split("/")[1]; - $window.location.href = '/booth/' + autheventid + '/vote/' + hash + '/' + msg; - }); - } - } else { - scope.sendingData = false; - scope.status = 'Not found'; - scope.error = $i18next('avRegistration.invalidCredentials', { support: ConfigService.contact.email }); - } - }) - .error(function(error) { - scope.sendingData = false; - scope.status = 'Registration error: ' + error.message; - scope.error = $i18next('avRegistration.invalidCredentials', { support: ConfigService.contact.email }); - }); + } + ); + } + else 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", autheventid) + .then(function onSuccess(response) { + var khmac = response.data['permission-token']; + var path = khmac.split(";")[1]; + var hash = path.split("/")[0]; + var msg = path.split("/")[1]; + $window.location.href = '/booth/' + autheventid + '/vote/' + hash + '/' + msg; + }); + } + } 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'); - scope.login_fields = Authmethod.getLoginFields(authevent); + if (!scope.isCensusQuery) { + scope.login_fields = Authmethod.getLoginFields(authevent); + } else { + scope.login_fields = Authmethod.getCensusQueryFields(authevent); + } scope.telIndex = -1; + scope.emailIndex = -1; scope.telField = null; + scope.allowUserResend = (function () { + 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, @@ -175,9 +301,15 @@ angular.module('avRegistration') el.value = null; el.disabled = false; } - if (el.type === "email" && scope.email !== null) { - el.value = scope.email; - el.disabled = true; + 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; @@ -188,6 +320,14 @@ angular.module('avRegistration') } 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; } return el; }); @@ -198,24 +338,29 @@ angular.module('avRegistration') return; } - scope.loginUser(true); + if (scope.method !== 'openid-connect') + { + scope.loginUser(true); + } }; scope.view = function(id) { Authmethod.viewEvent(id) - .success(function(data) { - if (data.status === "ok") { - scope.apply(data.events); + .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"; } - }) - .error(function(error) { - scope.status = 'Scan error: ' + error.message; + }, + function onError(response) { + scope.status = 'Scan error: ' + response.data.message; document.querySelector(".input-error").style.display = "block"; - }); + } + ); }; scope.view(autheventid); @@ -226,6 +371,54 @@ angular.module('avRegistration') 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', diff --git a/avRegistration/login-directive/login-directive.less b/avRegistration/login-directive/login-directive.less index 7618a49a..7c84bcb5 100644 --- a/avRegistration/login-directive/login-directive.less +++ b/avRegistration/login-directive/login-directive.less @@ -19,14 +19,6 @@ display: block; min-height: 38px; } - - .btn-success { - background-color: @av-primary-contrast; - } - - .btn-success:hover { - background-color: darken(@av-secondary-contrast, 25%); - } } .form-horizontal { @@ -43,6 +35,15 @@ .input-error { min-height: 20px; } + + .census-query { + padding-top: 20px; + font-size: 16px; + + .input-success .text-success { + color: green; + } + } } diff --git a/avRegistration/logout-controller/logout-controller.js b/avRegistration/logout-controller/logout-controller.js index 17f3f1c9..08063a89 100644 --- a/avRegistration/logout-controller/logout-controller.js +++ b/avRegistration/logout-controller/logout-controller.js @@ -16,18 +16,19 @@ **/ angular.module('avRegistration').controller('LogoutController', - function($scope, $stateParams, $filter, ConfigService, $i18next, $state, $cookies) { + function($scope, $stateParams, $filter, ConfigService, $i18next, $state, $cookies, Authmethod) { var adminId = ConfigService.freeAuthId; - var authevent = $cookies.authevent; - $cookies.user = ''; - $cookies.auth = ''; - $cookies.authevent = ''; - $cookies.userid = ''; - $cookies.isAdmin = false; + var authevent = Authmethod.getAuthevent(); + var postfix = "_authevent_" + authevent; + $cookies["user" + postfix] = ''; + $cookies["auth" + postfix] = ''; + $cookies["authevent_" + authevent] = ''; + $cookies["userid" + postfix] = ''; + $cookies["isAdmin" + postfix] = false; if (authevent === ConfigService.freeAuthId + '' || !authevent) { $state.go("admin.login"); } else { - $state.go("registration.login", {id: $cookies.authevent}); + $state.go("registration.login", {id: $cookies["authevent_" + authevent]}); } } ); diff --git a/avRegistration/openid-connect-directive/openid-connect-directive.html b/avRegistration/openid-connect-directive/openid-connect-directive.html new file mode 100644 index 00000000..e69de29b diff --git a/avRegistration/openid-connect-directive/openid-connect-directive.js b/avRegistration/openid-connect-directive/openid-connect-directive.js new file mode 100644 index 00000000..0a3f34fe --- /dev/null +++ b/avRegistration/openid-connect-directive/openid-connect-directive.js @@ -0,0 +1,268 @@ +/** + * 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["id_token_" + postfix]) + { + uri = uri.replace("__ID_TOKEN__", $cookies["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(); + delete $cookies["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 ''; + } + return decodeURIComponent(params[2].replace(/\+/g, ' ')); + } + + // validates the CSRF token + function validateCsrfToken() + { + if (!$cookies['openid-connect-csrf']) + { + redirectToLogin(); + return null; + } + + // validate csrf token format and data + var csrf = scope.csrf = angular.fromJson($cookies['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['openid-connect-csrf'] = null; + 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; + } + 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 postfix = "_authevent_" + scope.csrf.eventId; + $cookies["id_token_" + postfix] = data.id_token; + + // 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["authevent_" + scope.csrf.eventId] = scope.csrf.eventId; + $cookies["userid" + postfix] = response.data.username; + $cookies["user" + postfix] = response.data.username; + $cookies["auth" + postfix] = response.data['auth-token']; + $cookies["isAdmin" + postfix] = false; + Authmethod.setAuth($cookies["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(); + return; + } + }, + function onError(response) + { + // TODO: show error + redirectToLogin(); + return; + } + ); + } + + processOpenIdAuthCallback(); + } + return { + restrict: 'AE', + scope: true, + link: link, + templateUrl: 'avRegistration/openid-connect-directive/openid-connect-directive.html' + }; + }); diff --git a/avRegistration/openid-connect-directive/openid-connect-directive.less b/avRegistration/openid-connect-directive/openid-connect-directive.less new file mode 100644 index 00000000..e69de29b diff --git a/avRegistration/register-directive/register-directive.js b/avRegistration/register-directive/register-directive.js index ede6338c..7871f2cc 100644 --- a/avRegistration/register-directive/register-directive.js +++ b/avRegistration/register-directive/register-directive.js @@ -36,9 +36,12 @@ angular.module('avRegistration') scope.getLoginDetails = function (eventId) { if (!scope.admin) { - return {path: 'election.public.show.login', data: {id: eventId}}; + return { + path: 'election.public.show.login_email', + data: {id: eventId, email: scope.email} + }; } else { - return {path: 'admin.login', data:{}}; + return {path: 'admin.login_email', data:{email: scope.email}}; } }; @@ -52,45 +55,52 @@ angular.module('avRegistration') }; _.each(scope.register_fields, function (field) { data[field.name] = field.value; - if (field.name === 'email') { + 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) - .success(function(rcvData) { + .then( + function onSuccess(response) { details = scope.getLoginDetails(autheventid); - if (rcvData.status === "ok") { - scope.user = rcvData.user; + if (response.data.status === "ok") { + scope.user = response.data.user; StateDataService.go(details.path, details.data, data); - // TEST - scope.error = rcvData.msg || $sce.trustAsHtml($i18next('avRegistration.invalidRegisterData', { + 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 = rcvData.msg || $sce.trustAsHtml($i18next('avRegistration.invalidRegisterData', { + scope.error = response.data.msg || $sce.trustAsHtml($i18next('avRegistration.invalidRegisterData', { url: $state.href(details.path, details.data) })); } - }) - .error(function(error) { + }, + function onError(response) { details = scope.getLoginDetails(autheventid); scope.sendingData = false; - scope.status = 'Registration error: ' + error.message; + scope.status = 'Registration error: ' + response.data.message; - if (!!error.error_codename && error.error_codename === 'invalid-dni') { + if (!!response.data.error_codename && response.data.error_codename === 'invalid-dni') { scope.error = $sce.trustAsHtml($i18next('avRegistration.invalidRegisterDNI')); } else { - scope.error = error.msg || $sce.trustAsHtml($i18next('avRegistration.invalidRegisterData', { + scope.error = response.data.msg || $sce.trustAsHtml($i18next('avRegistration.invalidRegisterData', { url: $state.href(details.path, details.data) })); - if (error.msg === 'Invalid captcha') { + if (response.data.msg === 'Invalid captcha') { Authmethod.newCaptcha(); } } - }); + } + ); }; scope.goLogin = function(event) { @@ -117,7 +127,7 @@ angular.module('avRegistration') scope.authevent = authevent; // if registration is closed, redirect to login - if (authevent['census'] !== 'open') { + if (authevent['census'] !== 'open' || scope.method === 'openid-connect') { if (authevent['id'] === ConfigService.freeAuthId) { $state.go("admin.login"); } else { @@ -140,18 +150,20 @@ angular.module('avRegistration') scope.view = function(id) { Authmethod.viewEvent(id) - .success(function(data) { - if (data.status === "ok") { - scope.apply(data.events); + .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"; } - }) - .error(function(error) { - scope.status = 'Scan error: ' + error.message; + }, + function onError(response) { + scope.status = 'Scan error: ' + response.data.message; document.querySelector(".input-error").style.display = "block"; - }); + } + ); }; scope.view(autheventid); diff --git a/avUi/change-lang-directive/change-lang-directive.html b/avUi/change-lang-directive/change-lang-directive.html index 84141553..f6766281 100644 --- a/avUi/change-lang-directive/change-lang-directive.html +++ b/avUi/change-lang-directive/change-lang-directive.html @@ -4,6 +4,6 @@ diff --git a/avUi/change-lang-directive/change-lang-directive.js b/avUi/change-lang-directive/change-lang-directive.js index 5c1d5c22..a15dc117 100644 --- a/avUi/change-lang-directive/change-lang-directive.js +++ b/avUi/change-lang-directive/change-lang-directive.js @@ -24,6 +24,7 @@ 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 @@ -33,11 +34,16 @@ angular.module('avUi') scope.changeLang = function(lang) { $i18next.options.lng = lang; console.log("setting cookie"); + var cookieConf = { + expires: 360, + path: "/" + }; ipCookie( "lang", lang, - _.extend({expires: 360}, ConfigService.i18nextCookieOptions)); + _.extend(cookieConf, ConfigService.i18nextCookieOptions)); scope.deflang = lang; + angular.element('#ng-app').attr('lang', scope.deflang); // async load moment i18n angularLoad diff --git a/avUi/change-lang-directive/change-lang-directive.less b/avUi/change-lang-directive/change-lang-directive.less index 022daf87..87199e02 100644 --- a/avUi/change-lang-directive/change-lang-directive.less +++ b/avUi/change-lang-directive/change-lang-directive.less @@ -3,7 +3,7 @@ padding-left: 8px; &:hover { - background-color: @av-primary-contrast; + background-color: lightgray; } > a:hover { diff --git a/avUi/checker-service.js b/avUi/checker-service.js index 7e02617e..1f248874 100644 --- a/avUi/checker-service.js +++ b/avUi/checker-service.js @@ -125,16 +125,52 @@ angular.module('avUi') if (!pass) { error(item.check, {key: item.key}, item.postfix); } - } else if (item.check === "lambda") { if (!item.validator(d.data[item.key])) { var errorData = {key: item.key}; if (!angular.isUndefined(item.appendOnErrorLambda)) { errorData = item.appendOnErrorLambda(d.data[item.key]); } + 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(d.data[item.key]) || + angular.isString(d.data[item.key], item.postfix); + if (!pass) { + error(item.check, {key: item.key}, item.postfix); + } + + } else if (item.check === "array-length-if-defined") { + if (angular.isDefined(d.data[item.key])) { + itemMin = evalValue(item.min, d.data); + itemMax = evalValue(item.max, d.data); + + if (angular.isArray(d.data[item.key]) || angular.isString(d.data[item.key])) + { + min = angular.isUndefined(item.min) || d.data[item.key].length >= itemMin; + max = angular.isUndefined(item.max) || d.data[item.key].length <= itemMax; + pass = min && max; + if (!min) { + error( + "array-length-min", + {key: item.key, min: itemMin, num: d.data[item.key].length}, + item.postfix); + } + if (!max) { + var itemErrorData0 = {key: item.key, max: itemMax, num: d.data[item.key].length}; + error( + "array-length-max", + itemErrorData0, + item.postfix); + } + } + } } else if (item.check === "is-string") { pass = angular.isString(d.data[item.key], item.postfix); if (!pass) { @@ -247,6 +283,31 @@ angular.module('avUi') }); }), true); + } else if (item.check === "object-key-chain") { + pass = _.isString(item.key) && _.isObject(d.data[item.key]); + if (!!pass) { + var data = d.data[item.key]; + 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, + }); + }); + } } if (!pass && d.data.groupType === 'chain') { return false; diff --git a/avUi/collapsing-directive.js b/avUi/collapsing-directive.js index 7f0847e1..9de313d8 100644 --- a/avUi/collapsing-directive.js +++ b/avUi/collapsing-directive.js @@ -36,6 +36,15 @@ 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) { var val = null; @@ -47,16 +56,6 @@ angular.module('avUi') return val; } - function select(instance, el, selector) { - var val; - if (!!instance.parentSelector) { - val = el.closest(instance.parentSelector).find(selector); - } else { - val = angular.element(selector); - } - return val; - } - var checkCollapse = function(instance, el, options) { var maxHeight = select(instance, el, instance.maxHeightSelector).css("max-height"); var height = angular.element(el)[0].scrollHeight; diff --git a/avUi/documentation-directive/documentation-directive.html b/avUi/documentation-directive/documentation-directive.html index dd5bdbb5..bb57d4b9 100644 --- a/avUi/documentation-directive/documentation-directive.html +++ b/avUi/documentation-directive/documentation-directive.html @@ -1,7 +1,7 @@

-
    +
    + av-plugin-html + ng-bind-html="documentation_html_include">
diff --git a/avUi/documentation-directive/documentation-directive.js b/avUi/documentation-directive/documentation-directive.js index b73631ee..8cd9cec5 100644 --- a/avUi/documentation-directive/documentation-directive.js +++ b/avUi/documentation-directive/documentation-directive.js @@ -20,18 +20,18 @@ * loading config, showing results, showing error if needed. */ angular.module('avUi').controller('DocumentationUiController', - function($state, $stateParams, $http, $scope,$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 = ConfigService.documentation_html_include; + $scope.documentation_html_include = $sce.trustAsHtml(ConfigService.documentation_html_include); $scope.auths_url = '/election/' + $stateParams.id + '/public/authorities'; - $scope.legal_url = '/election/' + $stateParams.id + '/public/legal'; + $scope.election_id = $stateParams.id + ''; Authmethod.viewEvent($stateParams.id) - .success(function(data) { - if (data.status === "ok") { - $scope.authEvent = data.events; + .then(function(response) { + if (response.data.status === "ok") { + $scope.authEvent = response.data.events; } }); } @@ -41,6 +41,9 @@ angular.module('avUi') .directive('documentationDirective', function() { return { restrict: 'AE', + scope: { + extra: '=' + }, templateUrl: 'avUi/documentation-directive/documentation-directive.html', controller: 'DocumentationUiController' }; diff --git a/avUi/documentation-directive/documentation-directive.less b/avUi/documentation-directive/documentation-directive.less new file mode 100644 index 00000000..ad46e07b --- /dev/null +++ b/avUi/documentation-directive/documentation-directive.less @@ -0,0 +1,6 @@ + +[documentation-directive] { + .docu-ul { + margin-bottom: 0px; + } +} diff --git a/avUi/foot-directive/foot-directive.html b/avUi/foot-directive/foot-directive.html index 55e604a1..f484f24a 100644 --- a/avUi/foot-directive/foot-directive.html +++ b/avUi/foot-directive/foot-directive.html @@ -1,80 +1,14 @@
-
-
-
-

-

-
    -
  • -
  • -
- - -
- -
-

-

-
    -
  • -
  • -
  • -
  • -
  • -
-
- -
-

- -
-
-
+
diff --git a/avUi/foot-directive/foot-directive.js b/avUi/foot-directive/foot-directive.js index 2d0efaaa..0f195f92 100644 --- a/avUi/foot-directive/foot-directive.js +++ b/avUi/foot-directive/foot-directive.js @@ -23,6 +23,7 @@ angular.module('avUi') scope.social = ConfigService.social; scope.technology = ConfigService.technology; scope.legal = ConfigService.legal; + scope.organization = ConfigService.organization; } return { diff --git a/avUi/foot-directive/foot-directive.less b/avUi/foot-directive/foot-directive.less index fddc978d..d62dee79 100644 --- a/avUi/foot-directive/foot-directive.less +++ b/avUi/foot-directive/foot-directive.less @@ -1,14 +1,20 @@ -.commonfoot { - padding: 40px 0 200px 0; - background-color: @av-bg; -} +[av-foot] { + .commonfoot { + padding: 40px 0 200px 0; + background-color: @av-bg; + } -.commonfoot ul { - margin: 0px; - padding: 0px; - list-style: none; -} + .commonfoot ul { + margin: 0px; + padding: 0px; + list-style: none; + } -.commonfoot .social { - margin-top: 10px; -} + .commonfoot .social { + margin-top: 10px; + } + + .powered-by { + margin-left: 50px; + } +} \ No newline at end of file diff --git a/avUi/global.less b/avUi/global.less index b602f4d6..f7e19a9b 100644 --- a/avUi/global.less +++ b/avUi/global.less @@ -19,12 +19,16 @@ a:hover, a:active { border: 0; } +.modal-text-success { + color: #3c763d; +} + .text-brand-success { color: @brand-success; } .text-brand-warning { - color: @brand-warning; + color: #4a4a4a; } .text-brand-danger { @@ -147,4 +151,4 @@ a:hover, a:active { -moz-user-select: none; -ms-user-select: none; user-select: none; -} \ No newline at end of file +} diff --git a/avUi/percent-votes-service.js b/avUi/percent-votes-service.js index c9c80b3f..db4fbea8 100644 --- a/avUi/percent-votes-service.js +++ b/avUi/percent-votes-service.js @@ -44,9 +44,13 @@ angular.module('avUi') if (over === undefined || over === null) { over = question.answer_total_votes_percentage; } - if (over === "over-valid-votes") { + 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); }; diff --git a/bower.json b/bower.json deleted file mode 100755 index d9606ff3..00000000 --- a/bower.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "name": "avCommon", - "version" : "17.04", - "main": "index.html", - "ignore": [ - "tests", - "app.js", - "AUTHORS", - "bower_components", - "bower.json", - ".bowerrc", - "design", - "doc", - "e2e.conf.js", - ".editorconfig", - ".git", - ".gitignore", - "Gruntfile.js", - "gulpfile.js", - "index.html", - ".jshintrc", - "LICENSE", - "Makefile", - "node_modules", - ".npmignore", - "package.json", - "plugins", - "README.md", - "temp_data", - "test", - ".yo-rc.json" - ], - "dependencies": { - "bootstrap": "~3.1", - "font-awesome": "~4.3", - "moment": "~2.5", - "angular-mocks": "~1.2" - }, - "devDependencies": { - "jquery": "~2.0", - "underscore": "1.8.2", - "angular": "~1.2", - "angular-sanitize": "~1.2.25", - "angular-animate": "~1.2", - "angular-resource": "~1.2", - "angular-ui-router": "0.2.13", - "angular-ui-utils": "0.2.2", - "angular-bootstrap": "0.10", - "angular-moment": "~0.8.2", - "angular-cookies": "1.3.12", - "angular-drag-and-drop-lists": "~1.2.0", - "less.js": "~1.6", - "i18next": "1.9.0", - "ng-i18next": "0.4.2", - "ngInfiniteScroll": "1.2.0", - "papaparse": "~4.1.0", - "file-saver": "*", - "ng-file-upload": "3.0.7", - "ng-autofocus": "~0.0.1", - "angular-load": "~0.2.0", - "css-element-queries": "~0.0.2", - "rng-js": "~1.0.1", - "angular-cookie": "~4.0.9" - }, - "resolutions": { - "angular": "~1.2" - }, - "homepage": "https://github.com/agoravoting/agora-gui-common", - "authors": [ - "Víctor Ramírez " - ], - "description": "Library for agora-gui", - "moduleType": [], - "license": "MIT", - "private": true -} diff --git a/dist/appCommon-v17.04.js b/dist/appCommon-v17.04.js deleted file mode 100644 index 08b99434..00000000 --- a/dist/appCommon-v17.04.js +++ /dev/null @@ -1,1009 +0,0 @@ -angular.module("avRegistration", [ "ui.bootstrap", "ui.utils", "ui.router" ]), angular.module("avRegistration").config(function() {}), -angular.module("avRegistration").factory("Authmethod", [ "$http", "$cookies", "ConfigService", "$interval", function($http, $cookies, ConfigService, $interval) { - var backendUrl = ConfigService.authAPI, authId = ConfigService.freeAuthId, authmethod = {}; - return authmethod.captcha_code = null, authmethod.captcha_image_url = "", authmethod.captcha_status = "", - authmethod.admin = !1, authmethod.isAdmin = function() { - return authmethod.isLoggedIn() && authmethod.admin; - }, authmethod.isLoggedIn = function() { - var auth = $http.defaults.headers.common.Authorization; - return auth && auth.length > 0; - }, authmethod.signup = function(data, authevent) { - var eid = authevent || authId; - return $http.post(backendUrl + "auth-event/" + eid + "/register/", data); - }, authmethod.getUserInfo = function(userid) { - if (!authmethod.isLoggedIn()) { - var data = { - success: function() { - return data; - }, - error: function(func) { - return setTimeout(function() { - func({ - message: "not-logged-in" - }); - }, 0), data; - } - }; - return data; - } - return void 0 === userid ? $http.get(backendUrl + "user/", {}) : $http.get(backendUrl + "user/%d" % userid, {}); - }, authmethod.ping = function() { - if (!authmethod.isLoggedIn()) { - var data = { - success: function() { - return data; - }, - error: function(func) { - return setTimeout(function() { - func({ - message: "not-logged-in" - }); - }, 0), 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; - return delete data.authevent, $http.post(backendUrl + "auth-event/" + eid + "/authenticate/", data); - }, authmethod.resendAuthCode = function(data, eid) { - return $http.post(backendUrl + "auth-event/" + eid + "/resend_auth_code/", data); - }, authmethod.getPerm = function(perm, object_type, object_id) { - var data = { - permission: perm, - object_type: object_type, - object_id: object_id + "" - }; - 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) { - angular.isDefined(validation) || (validation = "enabled"); - var d = { - "field-validation": validation, - census: data - }; - return $http.post(backendUrl + "auth-event/" + id + "/census/", d); - }, authmethod.getCensus = function(id, params) { - return angular.isObject(params) ? $http.get(backendUrl + "auth-event/" + id + "/census/", { - params: params - }) : $http.get(backendUrl + "auth-event/" + id + "/census/"); - }, authmethod.getRegisterFields = function(viewEventData) { - var fields = angular.copy(viewEventData.extra_fields); - fields || (fields = []); - var found = !1; - _.each(fields, function(field) { - "sms" === viewEventData.auth_method && "tlf" === field.name ? ("text" === field.type && (field.type = "tlf"), - found = !0) : "email" === viewEventData.auth_method && "email" === field.name && (found = !0); - }), "sms" !== viewEventData.auth_method || found ? "email" !== viewEventData.auth_method || found ? "user-and-password" === viewEventData.auth_method && (fields.push({ - name: "email", - type: "email", - required: !0, - required_on_authentication: !0 - }), fields.push({ - name: "password", - type: "password", - required: !0, - required_on_authentication: !0 - })) : fields.push({ - name: "email", - type: "email", - required: !0, - required_on_authentication: !0 - }) : fields.push({ - name: "tlf", - type: "tlf", - required: !0, - required_on_authentication: !0 - }); - for (var i = 0; i < fields.length; i++) if ("captcha" === fields[i].type) { - var captcha = fields.splice(i, 1); - fields.push(captcha[0]); - break; - } - return fields; - }, authmethod.getLoginFields = function(viewEventData) { - var fields = authmethod.getRegisterFields(viewEventData); - "sms" !== viewEventData.auth_method && "email" !== viewEventData.auth_method || fields.push({ - name: "code", - type: "code", - required: !0, - 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; - } - return fields; - }, authmethod.newCaptcha = function(message) { - return authmethod.captcha_status = message, $http.get(backendUrl + "captcha/new/", {}).success(function(data) { - console.log(data), null !== data.captcha_code ? (authmethod.captcha_code = data.captcha_code, - authmethod.captcha_image_url = data.image_url) : authmethod.captcha_status = "Not found"; - }); - }, authmethod.test = function() { - return $http.get(backendUrl); - }, authmethod.setAuth = function(auth, isAdmin) { - return authmethod.admin = isAdmin, $http.defaults.headers.common.Authorization = auth, - authmethod.pingTimeout || ($interval.cancel(authmethod.pingTimeout), authmethod.launchPingDaemon(), - authmethod.pingTimeout = $interval(function() { - authmethod.launchPingDaemon(); - }, 500 * ConfigService.timeoutSeconds)), !1; - }, authmethod.electionsIds = function(page) { - return page || (page = 1), $http.get(backendUrl + "acl/mine/?object_type=AuthEvent&perm=edit|view&order=-pk&page=" + page); - }, authmethod.sendAuthCodes = function(eid, election, user_ids, auth_method, extra) { - var url = backendUrl + "auth-event/" + eid + "/census/send_auth/", data = {}; - return angular.isDefined(election) && (data.msg = election.census.config.msg, "email" === auth_method && (data.subject = election.census.config.subject)), - angular.isDefined(user_ids) && (data["user-ids"] = user_ids), angular.isDefined(auth_method) && (data["auth-method"] = auth_method), - extra && (data.extra = extra), $http.post(url, data); - }, authmethod.removeUsersIds = function(eid, election, user_ids) { - var url = backendUrl + "auth-event/" + eid + "/census/delete/", data = { - "user-ids": user_ids - }; - return $http.post(url, data); - }, authmethod.activateUsersIds = function(eid, election, user_ids) { - var url = backendUrl + "auth-event/" + eid + "/census/activate/", data = { - "user-ids": user_ids - }; - return $http.post(url, data); - }, authmethod.deactivateUsersIds = function(eid, election, user_ids) { - var url = backendUrl + "auth-event/" + eid + "/census/deactivate/", data = { - "user-ids": user_ids - }; - return $http.post(url, data); - }, authmethod.changeAuthEvent = function(eid, st) { - var url = backendUrl + "auth-event/" + eid + "/" + st + "/", data = {}; - return $http.post(url, data); - }, authmethod.launchPingDaemon = function() { - $cookies.isAdmin && authmethod.ping().success(function(data) { - $cookies.auth = data["auth-token"], authmethod.setAuth($cookies.auth, $cookies.isAdmin); - }); - }, authmethod; -} ]), angular.module("avRegistration").controller("LoginController", [ "$scope", "$stateParams", "$filter", "ConfigService", "$i18next", function($scope, $stateParams, $filter, ConfigService, $i18next) { - $scope.event_id = $stateParams.id, $scope.code = $stateParams.code, $scope.email = $stateParams.email; -} ]), angular.module("avRegistration").directive("avLogin", [ "Authmethod", "StateDataService", "$parse", "$state", "$cookies", "$i18next", "$window", "$timeout", "ConfigService", function(Authmethod, StateDataService, $parse, $state, $cookies, $i18next, $window, $timeout, ConfigService) { - function link(scope, element, attrs) { - var adminId = ConfigService.freeAuthId + "", autheventid = attrs.eventId; - scope.orgName = ConfigService.organization.orgName, $cookies.authevent && $cookies.authevent === adminId && autheventid === adminId && ($window.location.href = "/admin/elections"), - scope.sendingData = !1, scope.stateData = StateDataService.getData(), scope.code = null, - attrs.code && attrs.code.length > 0 && (scope.code = attrs.code), scope.email = null, - attrs.email && attrs.email.length > 0 && (scope.email = attrs.email), scope.isAdmin = !1, - autheventid === adminId && (scope.isAdmin = !0), scope.resendAuthCode = function(field) { - if (!scope.sendingData && "sms" === scope.method && -1 !== scope.telIndex && !scope.form["input" + scope.telIndex].$invalid) { - field.value = ""; - var data = {}; - data.tlf = scope.telField.value, scope.sendingData = !0, Authmethod.resendAuthCode(data, autheventid).success(function(rcvData) { - $timeout(scope.sendingDataTimeout, 3e3); - }).error(function(error) { - $timeout(scope.sendingDataTimeout, 3e3), scope.error = $i18next("avRegistration.errorSendingAuthCode"); - }); - } - }, scope.sendingDataTimeout = function() { - scope.sendingData = !1; - }, scope.loginUser = function(valid) { - if (valid && !scope.sendingData) { - var 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; - }), scope.sendingData = !0, Authmethod.login(data, autheventid).success(function(rcvData) { - "ok" === rcvData.status ? (scope.khmac = rcvData.khmac, $cookies.authevent = autheventid, - $cookies.userid = rcvData.username, $cookies.user = scope.email, $cookies.auth = rcvData["auth-token"], - $cookies.isAdmin = scope.isAdmin, Authmethod.setAuth($cookies.auth, scope.isAdmin), - scope.isAdmin ? Authmethod.getUserInfo().success(function(d) { - $cookies.user = d.email, $window.location.href = "/admin/elections"; - }).error(function(error) { - $window.location.href = "/admin/elections"; - }) : angular.isDefined(rcvData["redirect-to-url"]) ? $window.location.href = rcvData["redirect-to-url"] : Authmethod.getPerm("vote", "AuthEvent", autheventid).success(function(rcvData2) { - var khmac = rcvData2["permission-token"], path = khmac.split(";")[1], hash = path.split("/")[0], msg = path.split("/")[1]; - $window.location.href = "/booth/" + autheventid + "/vote/" + hash + "/" + msg; - })) : (scope.sendingData = !1, scope.status = "Not found", scope.error = $i18next("avRegistration.invalidCredentials", { - support: ConfigService.contact.email - })); - }).error(function(error) { - scope.sendingData = !1, scope.status = "Registration error: " + error.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 = "open" === authevent.census, - scope.login_fields = Authmethod.getLoginFields(authevent), scope.telIndex = -1, - scope.telField = null; - 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) : "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), el; - }); - _.filter(fields, function(el) { - return null !== el.value; - }).length === scope.login_fields.length && scope.loginUser(!0); - }, scope.view = function(id) { - Authmethod.viewEvent(id).success(function(data) { - "ok" === data.status ? scope.apply(data.events) : (scope.status = "Not found", document.querySelector(".input-error").style.display = "block"); - }).error(function(error) { - scope.status = "Scan error: " + error.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"); - }; - } - return { - restrict: "AE", - scope: !0, - link: link, - templateUrl: "avRegistration/login-directive/login-directive.html" - }; -} ]), angular.module("avRegistration").controller("LogoutController", [ "$scope", "$stateParams", "$filter", "ConfigService", "$i18next", "$state", "$cookies", function($scope, $stateParams, $filter, ConfigService, $i18next, $state, $cookies) { - var authevent = (ConfigService.freeAuthId, $cookies.authevent); - $cookies.user = "", $cookies.auth = "", $cookies.authevent = "", $cookies.userid = "", - $cookies.isAdmin = !1, authevent !== ConfigService.freeAuthId + "" && authevent ? $state.go("registration.login", { - id: $cookies.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) { - function link(scope, element, attrs) { - var autheventid = attrs.eventId; - scope.dnieurl = ConfigService.dnieUrl + autheventid + "/", scope.register = {}, - scope.sendingData = !1, scope.admin = !1, scope.email = null, attrs.email && attrs.email.length > 0 && (scope.email = attrs.email), - "admin" in attrs && (scope.admin = !0), scope.getLoginDetails = function(eventId) { - return scope.admin ? { - path: "admin.login", - data: {} - } : { - path: "election.public.show.login", - data: { - id: eventId - } - }; - }, scope.signUp = function(valid) { - if (valid) { - scope.sendingData = !0; - var data = { - captcha_code: Authmethod.captcha_code - }; - _.each(scope.register_fields, function(field) { - data[field.name] = field.value, "email" === field.name && (scope.email = field.value); - }); - var details; - Authmethod.signup(data, autheventid).success(function(rcvData) { - details = scope.getLoginDetails(autheventid), "ok" === rcvData.status ? (scope.user = rcvData.user, - StateDataService.go(details.path, details.data, data), scope.error = rcvData.msg || $sce.trustAsHtml($i18next("avRegistration.invalidRegisterData", { - url: $state.href(details.path, details.data) - }))) : (scope.sendingData = !1, scope.status = "Not found", scope.error = rcvData.msg || $sce.trustAsHtml($i18next("avRegistration.invalidRegisterData", { - url: $state.href(details.path, details.data) - }))); - }).error(function(error) { - details = scope.getLoginDetails(autheventid), scope.sendingData = !1, scope.status = "Registration error: " + error.message, - error.error_codename && "invalid-dni" === error.error_codename ? scope.error = $sce.trustAsHtml($i18next("avRegistration.invalidRegisterDNI")) : (scope.error = error.msg || $sce.trustAsHtml($i18next("avRegistration.invalidRegisterData", { - url: $state.href(details.path, details.data) - })), "Invalid captcha" === error.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, scope.authevent = authevent, - "open" !== authevent.census && (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).success(function(data) { - "ok" === data.status ? scope.apply(data.events) : (scope.status = "Not found", document.querySelector(".input-error").style.display = "block"); - }).error(function(error) { - scope.status = "Scan error: " + error.message, document.querySelector(".input-error").style.display = "block"; - }); - }, scope.view(autheventid); - } - return { - restrict: "AE", - scope: !0, - link: link, - templateUrl: "avRegistration/register-directive/register-directive.html" - }; -} ]), angular.module("avRegistration").factory("Patterns", function() { - var patterns = {}; - return 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,}))$/ : /.*/; - }, patterns; -}), angular.module("avRegistration").directive("avrField", [ "$state", function($state) { - function link(scope, element, attrs) { - console.log("type = " + scope.field.type), scope.index = attrs.index; - } - return { - restrict: "AE", - scope: !0, - link: link, - templateUrl: "avRegistration/field-directive/field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrEmailField", [ "$state", "Patterns", function($state, Patterns) { - function link(scope, element, attrs) { - scope.patterns = function(name) { - return Patterns.get(name); - }; - } - return { - restrict: "AE", - link: link, - scope: !0, - templateUrl: "avRegistration/fields/email-field-directive/email-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) { - function link(scope, element, attrs) { - angular.isUndefined(scope.field.regex) ? scope.re = new RegExp("") : scope.re = new RegExp(scope.field.regex), - scope.getRe = function(value) { - return scope.re; - }; - } - return { - restrict: "AE", - link: link, - scope: !0, - templateUrl: "avRegistration/fields/text-field-directive/text-field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrDniField", [ "$state", function($state) { - function link(scope, element, attrs) { - scope.dni_re = /^[XYZ]?\d{7,8}[A-Z]$/, scope.validateDni = function(str) { - str || (str = ""), str = str.toUpperCase().replace(/\s/, ""); - var prefix = str.charAt(0), index = "XYZ".indexOf(prefix); - return index > -1 && (index, str = str.substr(1), "Y" === prefix ? str = "1" + str : "Z" === prefix && (str = "2" + str)), - "TRWAGMYFPDXBNJZSQVHLCKE".charAt(parseInt(str, 10) % 23) === str.charAt(str.length - 1); - }; - } - return { - restrict: "AE", - link: link, - scope: !0, - templateUrl: "avRegistration/fields/dni-field-directive/dni-field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrCodeField", [ "$state", "Plugins", function($state, Plugins) { - function link(scope, element, attrs) { - scope.codePattern = /[abcdefghjklmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789-]{8,9}/, - scope.showResendAuthCode = function() { - var data = { - showUserSendAuthCode: !0 - }; - return Plugins.hook("hide-user-send-auth-code", data), data.showUserSendAuthCode; - }; - } - return { - restrict: "AE", - scope: !0, - link: link, - templateUrl: "avRegistration/fields/code-field-directive/code-field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrTelField", [ "$state", function($state) { - function link(scope, element, attrs) { - scope.tlfPattern = /^[+]?\d{9,14}$/; - } - return { - restrict: "AE", - scope: !0, - link: link, - templateUrl: "avRegistration/fields/tel-field-directive/tel-field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrBoolField", [ "$state", function($state) { - return { - restrict: "AE", - scope: !0, - templateUrl: "avRegistration/fields/bool-field-directive/bool-field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrIntField", [ "$state", function($state) { - function link(scope, element, attrs) { - angular.isUndefined(scope.field.regex) ? scope.re = new RegExp("") : scope.re = new RegExp(scope.field.regex), - scope.getRe = function(value) { - return scope.re; - }; - } - return { - restrict: "AE", - link: link, - scope: !0, - templateUrl: "avRegistration/fields/int-field-directive/int-field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrCaptchaField", [ "Authmethod", "$state", "$interval", function(Authmethod, $state, $interval) { - function link(scope, element, attrs) { - scope.authMethod = Authmethod, Authmethod.newCaptcha(""); - } - return { - restrict: "AE", - scope: !0, - link: link, - templateUrl: "avRegistration/fields/captcha-field-directive/captcha-field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrTextareaField", [ "$state", function($state) { - return { - restrict: "AE", - scope: !0, - templateUrl: "avRegistration/fields/textarea-field-directive/textarea-field-directive.html" - }; -} ]), angular.module("avRegistration").directive("avrImageField", [ "$state", "$timeout", 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: link, - scope: !0, - templateUrl: "avRegistration/fields/image-field-directive/image-field-directive.html" - }; -} ]), angular.module("avRegistration").factory("Plugins", function() { - var plugins = {}; - return plugins.plugins = { - list: [] - }, 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("avSimpleError", [ "$resource", "$window", function($resource, $window) { - function link(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(); - }); - } - return { - restrict: "AE", - scope: {}, - link: link, - transclude: !0, - templateUrl: "avUi/simple-error-directive/simple-error-directive.html" - }; -} ]), angular.module("avUi").directive("avChangeLang", [ "$i18next", "ipCookie", "angularLoad", "amMoment", "ConfigService", function($i18next, ipCookie, angularLoad, amMoment, ConfigService) { - function link(scope, element, attrs) { - scope.deflang = window.i18n.lng(), scope.langs = $i18next.options.lngWhitelist, - scope.changeLang = function(lang) { - $i18next.options.lng = lang, console.log("setting cookie"), ipCookie("lang", lang, _.extend({ - expires: 360 - }, ConfigService.i18nextCookieOptions)), scope.deflang = lang, angularLoad.loadScript(ConfigService.base + "/locales/moment/" + lang + ".js").then(function() { - amMoment.changeLocale(lang); - }); - }; - } - return { - restrict: "AE", - scope: {}, - link: link, - templateUrl: "avUi/change-lang-directive/change-lang-directive.html" - }; -} ]), angular.module("avUi").directive("avAffixBottom", [ "$window", "$timeout", "$parse", function($window, $timeout, $parse) { - var affixBottomClass = "affix-bottom", checkPosition = function(scope, instance, el, options) { - var affix = !1, elHeight = $(el).actual("height"); - ($("body").height() + elHeight > window.innerHeight || instance.forceAffixWidth && window.innerWidth < instance.forceAffixWidth) && (affix = affixBottomClass), - instance.affixed !== affix && (instance.affix = affix, instance.setIsAffix(scope, affix), - el.removeClass("hidden"), affix ? (el.addClass(affixBottomClass), $(el).parent().css("margin-bottom", elHeight + "px")) : (el.removeClass(affixBottomClass), - $(el).parent().css("margin-bottom", instance.defaultBottomMargin))); - }; - return { - restrict: "EAC", - link: function(scope, iElement, iAttrs) { - function callCheckPos() { - timeout = $timeout(function() { - $timeout.cancel(timeout), checkPosition(scope, instance, iElement, iAttrs); - }, 100); - } - var instance = { - affix: !1, - getIsAffix: null, - setIsAffix: angular.noop, - defaultBottomMargin: iElement.css("margin-bottom"), - forceAffixWidth: parseInt(iAttrs.forceAffixWidth, 10) - }; - iAttrs.avAffixBottom.length > 0 && (instance.getIsAffix = $parse(iAttrs.avAffixBottom), - instance.setIsAffix = instance.getIsAffix.assign); - var timeout; - 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); - } - }; -} ]), angular.module("avUi").directive("avAutoHeight", [ "$window", "$timeout", function($window, $timeout) { - return { - link: function(scope, element, attrs) { - var sibling, recalculate, 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"); - }, 100); - }, scope.$watch(function() { - return sibling().height(); - }, function(newValue, oldValue) { - recalculate(); - }), recalculate(); - } - }; -} ]), angular.module("avUi").directive("avAffixTopOffset", [ "$window", "$timeout", "$parse", function($window, $timeout, $parse) { - var affixClass = "affix-top", checkPosition = function(scope, 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(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"), void 0 !== options.affixPlaceholder && $(options.affixPlaceholder).addClass("affixed")) : (el.removeClass(affixClass), - el.attr("style", ""), void 0 !== options.affixPlaceholder && $(options.affixPlaceholder).removeClass("affixed")))); - }; - return { - restrict: "EAC", - link: function(scope, iElement, iAttrs) { - function callCheckPos() { - checkPosition(scope, instance, iElement, iAttrs); - } - function resize() { - iElement.removeClass(affixClass), iElement.attr("style", ""), instance.affix = !1, - instance.scrollAffix = null, $timeout(function() { - instance.baseOffset = iElement.offset(), instance.baseWidth = iElement.width(), - callCheckPos(); - }, 100); - } - var instance = { - affix: !1, - scrollAffix: null, - baseOffset: iElement.offset(), - baseWidth: iElement.width(), - avAffixTopOffset: parseInt(iAttrs.avAffixTopOffset, 10) - }; - callCheckPos(), angular.element($window).on("scroll", callCheckPos), angular.element($window).on("resize", resize); - } - }; -} ]), angular.module("avUi").directive("avAffixTop", [ "$window", "$timeout", function($window, $timeout) { - var updateMargin = function(el, options) { - var minHeight = parseInt(options.minHeight), 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) { - function updateMarginTimeout() { - timeout = $timeout(function() { - $timeout.cancel(timeout), updateMargin(iElement, iAttrs); - }, 100); - } - updateMargin(iElement, iAttrs), void 0 === iAttrs.minHeight && (iAttrs.minHeight = "20"); - var timeout; - 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 collapseEl(instance, el) { - return instance.collapseSelector ? select(instance, el, instance.collapseSelector) : angular.element(el); - } - function select(instance, el, selector) { - return instance.parentSelector ? el.closest(instance.parentSelector).find(selector) : angular.element(selector); - } - var checkCollapse = function(instance, el, options) { - var maxHeight = select(instance, el, instance.maxHeightSelector).css("max-height"), height = angular.element(el)[0].scrollHeight; - if (-1 === maxHeight.indexOf("px")) return void console.log("invalid non-pixels max-height for " + instance.maxHeightSelector); - if (maxHeight = parseInt(maxHeight.replace("px", "")), height > maxHeight) { - if (instance.isCollapsed) return; - instance.isCollapsed = !0, collapseEl(instance, el).addClass("collapsed"), select(instance, el, instance.toggleSelector).removeClass("hidden in"); - } else { - if (!instance.isCollapsed) return; - instance.isCollapsed = !1, collapseEl(instance, el).removeClass("collapsed"), select(instance, el, instance.toggleSelector).addClass("hidden"); - } - }, toggleCollapse = function(instance, el, options) { - 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; - }; - return { - restrict: "EAC", - link: function(scope, iElement, iAttrs) { - function callCheck() { - timeout = $timeout(function() { - $timeout.cancel(timeout), checkCollapse(instance, iElement, iAttrs); - }, 100); - } - function launchToggle() { - toggleCollapse(instance, iElement, iAttrs); - } - var timeout, instance = { - isCollapsed: !1, - maxHeightSelector: iAttrs.avCollapsing, - toggleSelector: iAttrs.toggleSelector, - parentSelector: iAttrs.parentSelector, - collapseSelector: iAttrs.collapseSelector - }; - callCheck(), angular.element($window).bind("resize", callCheck), angular.element(iElement).bind("resize", callCheck), - angular.element(instance.toggleSelector).bind("click", launchToggle); - } - }; -} ]), angular.module("avUi").directive("avRecompile", [ "$compile", "$parse", function($compile, $parse) { - "use strict"; - function getElementAsHtml(el) { - return angular.element("").append(el.clone()).html(); - } - return { - scope: !0, - compile: function(el) { - var template = getElementAsHtml(el); - return function(scope, $el, attrs) { - var stopWatching = scope.$parent.$watch(attrs.avRecompile, function(_new, _old) { - var useBoolean = attrs.hasOwnProperty("useBoolean"); - if ((!useBoolean || _new && "false" !== _new) && (useBoolean || _new && _new !== _old)) { - useBoolean && $parse(attrs.kcdRecompile).assign(scope.$parent, !1); - var newEl = $compile(template)(scope.$parent); - $el.replaceWith(newEl), stopWatching(), scope.$destroy(); - } - }); - }; - } - }; -} ]), angular.module("avUi").directive("avDebounce", [ "$timeout", function($timeout) { - return { - restrict: "A", - require: "ngModel", - priority: 99, - link: function(scope, elm, attr, ngModelCtrl) { - if ("radio" !== attr.type && "checkbox" !== attr.type) { - 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() { - return function() { - try { - return window.self !== window.top; - } catch (e) { - return !0; - } - }; -}), angular.module("avUi").service("PercentVotesService", function() { - return function(total_votes, question, over, format) { - function print(num) { - return "str" === format ? num.toFixed(2) + "%" : 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 void 0 !== over && null !== over || (over = question.answer_total_votes_percentage), - "over-valid-votes" === over && (base = question.totals.valid_votes), print(100 * total_votes / base); - }; -}), angular.module("avUi").service("CheckerService", function() { - function checker(d) { - function evalValue(code, $value) { - return angular.isString(code) ? eval(code) : code; - } - function sumStrs(str1, str2) { - var ret = ""; - return angular.isString(str1) && (ret = str1), angular.isString(str2) && (ret += str2), - ret; - } - function error(errorKey, errorData, postfix) { - angular.extend(errorData, d.errorData), d.onError(_.reduce([ d.prefix, errorKey, postfix ], sumStrs, ""), errorData); - } - angular.isUndefined(d.errorData) && (d.errorData = {}); - var ret = _.every(d.checks, function(item) { - var itemMin, itemMax, max, min, pass = !0; - if ("is-int" === item.check) (pass = angular.isNumber(d.data[item.key], item.postfix)) || error(item.check, { - key: item.key - }, item.postfix); else if ("is-array" === item.check) (pass = angular.isArray(d.data[item.key], item.postfix)) || error(item.check, { - key: item.key - }, item.postfix); else if ("lambda" === item.check) { - if (!item.validator(d.data[item.key])) { - var errorData = { - key: item.key - }; - angular.isUndefined(item.appendOnErrorLambda) || (errorData = item.appendOnErrorLambda(d.data[item.key])), - error(item.check, errorData, item.postfix); - } - } else if ("is-string" === item.check) (pass = angular.isString(d.data[item.key], item.postfix)) || error(item.check, { - key: item.key - }, item.postfix); else if ("array-length" === item.check) { - if (itemMin = evalValue(item.min, d.data), itemMax = evalValue(item.max, d.data), - (angular.isArray(d.data[item.key]) || angular.isString(d.data[item.key])) && (min = angular.isUndefined(item.min) || d.data[item.key].length >= itemMin, - max = angular.isUndefined(item.max) || d.data[item.key].length <= itemMax, pass = min && max, - min || error("array-length-min", { - key: item.key, - min: itemMin, - num: d.data[item.key].length - }, item.postfix), !max)) { - var itemErrorData = { - key: item.key, - max: itemMax, - num: d.data[item.key].length - }; - error("array-length-max", itemErrorData, item.postfix); - } - } else "int-size" === item.check ? (itemMin = evalValue(item.min, d.data), itemMax = evalValue(item.max, d.data), - min = angular.isUndefined(item.min) || d.data[item.key] >= itemMin, max = angular.isUndefined(item.max) || d.data[item.key] <= itemMax, - pass = min && max, min || error("int-size-min", { - key: item.key, - min: itemMin, - value: d.data[item.key] - }, item.postfix), max || error("int-size-max", { - key: item.key, - max: itemMax, - value: d.data[item.key] - }, item.postfix)) : "group-chain" === item.check ? 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) - }); - })) : "array-key-group-chain" === item.check ? pass = _.every(d.data[item.key], 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) { - 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) - }); - }), !0)); - return !(!pass && "chain" === d.data.groupType); - }); - return ret; - } - return checker; -}), angular.module("avUi").service("AddDotsToIntService", function() { - return function(number, fixedDigits) { - angular.isNumber(fixedDigits) && fixedDigits >= 0 && (number = number.toFixed(parseInt(fixedDigits))); - var number_str = (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 && reverse > 0 && i > 0 && (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; - var lastIndex = originString.indexOf(searchString); - return -1 !== lastIndex && lastIndex === originString.length - searchString.length; - }; -}), angular.module("avUi").service("StateDataService", [ "$state", function($state) { - var data = {}; - return { - 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) { - return { - restrict: "A", - link: function(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(x) { - var tree = angular.element("
" + x + "
"); - return tree.find("a").attr("target", "_blank"), angular.element("
").append(tree).html(); - }; -}), angular.module("avUi").filter("htmlToText", function() { - return function(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, this, { - $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, this, { - $scope: scope, - $element: element, - $attrs: attrs - }); - } ], $delegate; - } ]); -} ]), angular.module("avUi").controller("DocumentationUiController", [ "$state", "$stateParams", "$http", "$scope", "$i18next", "ConfigService", "InsideIframeService", "Authmethod", function($state, $stateParams, $http, $scope, $i18next, ConfigService, InsideIframeService, Authmethod) { - $scope.inside_iframe = InsideIframeService(), $scope.documentation = ConfigService.documentation, - $scope.documentation.security_contact = ConfigService.legal.security_contact, $scope.documentation_html_include = ConfigService.documentation_html_include, - $scope.auths_url = "/election/" + $stateParams.id + "/public/authorities", $scope.legal_url = "/election/" + $stateParams.id + "/public/legal", - Authmethod.viewEvent($stateParams.id).success(function(data) { - "ok" === data.status && ($scope.authEvent = data.events); - }); -} ]), angular.module("avUi").directive("documentationDirective", function() { - return { - restrict: "AE", - templateUrl: "avUi/documentation-directive/documentation-directive.html", - controller: "DocumentationUiController" - }; -}), angular.module("avUi").directive("avFoot", [ "ConfigService", function(ConfigService) { - function link(scope, element, attrs) { - scope.contact = ConfigService.contact, scope.social = ConfigService.social, scope.technology = ConfigService.technology, - scope.legal = ConfigService.legal; - } - return { - 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", "angular-date-picker-polyfill", "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(); - }); -} ]), 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; - }; -}), 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", '

'), - $templateCache.put("avRegistration/field-directive/field-directive.html", '
'), - $templateCache.put("avRegistration/fields/bool-field-directive/bool-field-directive.html", '

'), - $templateCache.put("avRegistration/fields/captcha-field-directive/captcha-field-directive.html", '

{{field.help}}

{{authMethod.captcha_status}}
'), - $templateCache.put("avRegistration/fields/code-field-directive/code-field-directive.html", '

'), - $templateCache.put("avRegistration/fields/dni-field-directive/dni-field-directive.html", '

'), - $templateCache.put("avRegistration/fields/email-field-directive/email-field-directive.html", '
'), - $templateCache.put("avRegistration/fields/image-field-directive/image-field-directive.html", '

'), - $templateCache.put("avRegistration/fields/int-field-directive/int-field-directive.html", '

{{field.help}}

'), - $templateCache.put("avRegistration/fields/password-field-directive/password-field-directive.html", '

'), - $templateCache.put("avRegistration/fields/tel-field-directive/tel-field-directive.html", '

'), - $templateCache.put("avRegistration/fields/text-field-directive/text-field-directive.html", '

{{field.help}}

'), - $templateCache.put("avRegistration/fields/textarea-field-directive/textarea-field-directive.html", '

{{field.help}}

'), - $templateCache.put("avRegistration/loading.html", '

'), - $templateCache.put("avRegistration/login-controller/login-controller.html", '
'), - $templateCache.put("avRegistration/login-directive/login-directive.html", '

{{ error }}
avRegistration.fillValidFormText
'), - $templateCache.put("avRegistration/register-controller/register-controller.html", '
'), - $templateCache.put("avRegistration/register-directive/register-directive.html", '

avRegistration.fillValidFormText
'), - $templateCache.put("avRegistration/success.html", '

'), - $templateCache.put("avUi/change-lang-directive/change-lang-directive.html", ''), - $templateCache.put("avUi/documentation-directive/documentation-directive.html", '

'), - $templateCache.put("avUi/foot-directive/foot-directive.html", '

\x3c!-- social links --\x3e

'), - $templateCache.put("avUi/simple-error-directive/simple-error-directive.html", '
'), - $templateCache.put("test/test_booth_widget.html", 'Test frame \ No newline at end of file +Agora Voting
Page is taking some time to load, wait a moment please. If it takes too long, please check that you have javascript activated, try with another browser or contact us.
For security reasons, your browser is unsupported. Please use a newer web browser (if you are using Internet Explorer, use version 9 or newer).
\ No newline at end of file diff --git a/dist/intlTelInput.css b/dist/intlTelInput.css new file mode 100644 index 00000000..a2276914 --- /dev/null +++ b/dist/intlTelInput.css @@ -0,0 +1,979 @@ +/** + * Variables declared here can be overridden by consuming applications, with + * the help of the `!default` flag. + * + * @example + * // overriding $hoverColor + * $hoverColor: rgba(red, 0.05); + * + * // overriding image path + * $flagsImagePath: "images/"; + * + * // import the scss file after the overrides + * @import "bower_component/intl-tel-input/src/css/intlTelInput"; + */ +.intl-tel-input { + position: relative; + display: inline-block; } + .intl-tel-input * { + box-sizing: border-box; + -moz-box-sizing: border-box; } + .intl-tel-input .hide { + display: none; } + .intl-tel-input .v-hide { + visibility: hidden; } + .intl-tel-input input, .intl-tel-input input[type=text], .intl-tel-input input[type=tel] { + position: relative; + z-index: 0; + margin-top: 0 !important; + margin-bottom: 0 !important; + padding-right: 36px; + margin-right: 0; } + .intl-tel-input .flag-container { + position: absolute; + top: 0; + bottom: 0; + right: 0; + padding: 1px; } + .intl-tel-input .selected-flag { + z-index: 1; + position: relative; + width: 36px; + height: 100%; + padding: 0 0 0 8px; } + .intl-tel-input .selected-flag .iti-flag { + position: absolute; + top: 0; + bottom: 0; + margin: auto; } + .intl-tel-input .selected-flag .iti-arrow { + position: absolute; + top: 50%; + margin-top: -2px; + right: 6px; + width: 0; + height: 0; + border-left: 3px solid transparent; + border-right: 3px solid transparent; + border-top: 4px solid #555; } + .intl-tel-input .selected-flag .iti-arrow.up { + border-top: none; + border-bottom: 4px solid #555; } + .intl-tel-input .country-list { + position: absolute; + z-index: 2; + list-style: none; + text-align: left; + padding: 0; + margin: 0 0 0 -1px; + box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2); + background-color: white; + border: 1px solid #CCC; + white-space: nowrap; + max-height: 200px; + overflow-y: scroll; } + .intl-tel-input .country-list.dropup { + bottom: 100%; + margin-bottom: -1px; } + .intl-tel-input .country-list .flag-box { + display: inline-block; + width: 20px; } + @media (max-width: 500px) { + .intl-tel-input .country-list { + white-space: normal; } } + .intl-tel-input .country-list .divider { + padding-bottom: 5px; + margin-bottom: 5px; + border-bottom: 1px solid #CCC; } + .intl-tel-input .country-list .country { + padding: 5px 10px; } + .intl-tel-input .country-list .country .dial-code { + color: #999; } + .intl-tel-input .country-list .country.highlight { + background-color: rgba(0, 0, 0, 0.05); } + .intl-tel-input .country-list .flag-box, .intl-tel-input .country-list .country-name, .intl-tel-input .country-list .dial-code { + vertical-align: middle; } + .intl-tel-input .country-list .flag-box, .intl-tel-input .country-list .country-name { + margin-right: 6px; } + .intl-tel-input.allow-dropdown input, .intl-tel-input.allow-dropdown input[type=text], .intl-tel-input.allow-dropdown input[type=tel], .intl-tel-input.separate-dial-code input, .intl-tel-input.separate-dial-code input[type=text], .intl-tel-input.separate-dial-code input[type=tel] { + padding-right: 6px; + padding-left: 52px; + margin-left: 0; } + .intl-tel-input.allow-dropdown .flag-container, .intl-tel-input.separate-dial-code .flag-container { + right: auto; + left: 0; } + .intl-tel-input.allow-dropdown .selected-flag, .intl-tel-input.separate-dial-code .selected-flag { + width: 46px; } + .intl-tel-input.allow-dropdown .flag-container:hover { + cursor: pointer; } + .intl-tel-input.allow-dropdown .flag-container:hover .selected-flag { + background-color: rgba(0, 0, 0, 0.05); } + .intl-tel-input.allow-dropdown input[disabled] + .flag-container:hover, .intl-tel-input.allow-dropdown input[readonly] + .flag-container:hover { + cursor: default; } + .intl-tel-input.allow-dropdown input[disabled] + .flag-container:hover .selected-flag, .intl-tel-input.allow-dropdown input[readonly] + .flag-container:hover .selected-flag { + background-color: transparent; } + .intl-tel-input.separate-dial-code .selected-flag { + background-color: rgba(0, 0, 0, 0.05); + display: table; } + .intl-tel-input.separate-dial-code .selected-dial-code { + display: table-cell; + vertical-align: middle; + padding-left: 28px; } + .intl-tel-input.separate-dial-code.iti-sdc-2 input, .intl-tel-input.separate-dial-code.iti-sdc-2 input[type=text], .intl-tel-input.separate-dial-code.iti-sdc-2 input[type=tel] { + padding-left: 66px; } + .intl-tel-input.separate-dial-code.iti-sdc-2 .selected-flag { + width: 60px; } + .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-2 input, .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-2 input[type=text], .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-2 input[type=tel] { + padding-left: 76px; } + .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-2 .selected-flag { + width: 70px; } + .intl-tel-input.separate-dial-code.iti-sdc-3 input, .intl-tel-input.separate-dial-code.iti-sdc-3 input[type=text], .intl-tel-input.separate-dial-code.iti-sdc-3 input[type=tel] { + padding-left: 74px; } + .intl-tel-input.separate-dial-code.iti-sdc-3 .selected-flag { + width: 68px; } + .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-3 input, .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-3 input[type=text], .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-3 input[type=tel] { + padding-left: 84px; } + .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-3 .selected-flag { + width: 78px; } + .intl-tel-input.separate-dial-code.iti-sdc-4 input, .intl-tel-input.separate-dial-code.iti-sdc-4 input[type=text], .intl-tel-input.separate-dial-code.iti-sdc-4 input[type=tel] { + padding-left: 82px; } + .intl-tel-input.separate-dial-code.iti-sdc-4 .selected-flag { + width: 76px; } + .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-4 input, .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-4 input[type=text], .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-4 input[type=tel] { + padding-left: 92px; } + .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-4 .selected-flag { + width: 86px; } + .intl-tel-input.separate-dial-code.iti-sdc-5 input, .intl-tel-input.separate-dial-code.iti-sdc-5 input[type=text], .intl-tel-input.separate-dial-code.iti-sdc-5 input[type=tel] { + padding-left: 90px; } + .intl-tel-input.separate-dial-code.iti-sdc-5 .selected-flag { + width: 84px; } + .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-5 input, .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-5 input[type=text], .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-5 input[type=tel] { + padding-left: 100px; } + .intl-tel-input.separate-dial-code.allow-dropdown.iti-sdc-5 .selected-flag { + width: 94px; } + .intl-tel-input.iti-container { + position: absolute; + top: -1000px; + left: -1000px; + z-index: 1060; + padding: 1px; } + .intl-tel-input.iti-container:hover { + cursor: pointer; } + +.iti-mobile .intl-tel-input.iti-container { + top: 30px; + bottom: 30px; + left: 30px; + right: 30px; + position: fixed; } + +.iti-mobile .intl-tel-input .country-list { + max-height: 100%; + width: 100%; } + .iti-mobile .intl-tel-input .country-list .country { + padding: 10px 10px; + line-height: 1.5em; } + +.iti-flag { + width: 20px; } + .iti-flag.be { + width: 18px; } + .iti-flag.ch { + width: 15px; } + .iti-flag.mc { + width: 19px; } + .iti-flag.ne { + width: 18px; } + .iti-flag.np { + width: 13px; } + .iti-flag.va { + width: 15px; } + @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2 / 1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { + .iti-flag { + background-size: 5630px 15px; } } + .iti-flag.ac { + height: 10px; + background-position: 0px 0px; } + .iti-flag.ad { + height: 14px; + background-position: -22px 0px; } + .iti-flag.ae { + height: 10px; + background-position: -44px 0px; } + .iti-flag.af { + height: 14px; + background-position: -66px 0px; } + .iti-flag.ag { + height: 14px; + background-position: -88px 0px; } + .iti-flag.ai { + height: 10px; + background-position: -110px 0px; } + .iti-flag.al { + height: 15px; + background-position: -132px 0px; } + .iti-flag.am { + height: 10px; + background-position: -154px 0px; } + .iti-flag.ao { + height: 14px; + background-position: -176px 0px; } + .iti-flag.aq { + height: 14px; + background-position: -198px 0px; } + .iti-flag.ar { + height: 13px; + background-position: -220px 0px; } + .iti-flag.as { + height: 10px; + background-position: -242px 0px; } + .iti-flag.at { + height: 14px; + background-position: -264px 0px; } + .iti-flag.au { + height: 10px; + background-position: -286px 0px; } + .iti-flag.aw { + height: 14px; + background-position: -308px 0px; } + .iti-flag.ax { + height: 13px; + background-position: -330px 0px; } + .iti-flag.az { + height: 10px; + background-position: -352px 0px; } + .iti-flag.ba { + height: 10px; + background-position: -374px 0px; } + .iti-flag.bb { + height: 14px; + background-position: -396px 0px; } + .iti-flag.bd { + height: 12px; + background-position: -418px 0px; } + .iti-flag.be { + height: 15px; + background-position: -440px 0px; } + .iti-flag.bf { + height: 14px; + background-position: -460px 0px; } + .iti-flag.bg { + height: 12px; + background-position: -482px 0px; } + .iti-flag.bh { + height: 12px; + background-position: -504px 0px; } + .iti-flag.bi { + height: 12px; + background-position: -526px 0px; } + .iti-flag.bj { + height: 14px; + background-position: -548px 0px; } + .iti-flag.bl { + height: 14px; + background-position: -570px 0px; } + .iti-flag.bm { + height: 10px; + background-position: -592px 0px; } + .iti-flag.bn { + height: 10px; + background-position: -614px 0px; } + .iti-flag.bo { + height: 14px; + background-position: -636px 0px; } + .iti-flag.bq { + height: 14px; + background-position: -658px 0px; } + .iti-flag.br { + height: 14px; + background-position: -680px 0px; } + .iti-flag.bs { + height: 10px; + background-position: -702px 0px; } + .iti-flag.bt { + height: 14px; + background-position: -724px 0px; } + .iti-flag.bv { + height: 15px; + background-position: -746px 0px; } + .iti-flag.bw { + height: 14px; + background-position: -768px 0px; } + .iti-flag.by { + height: 10px; + background-position: -790px 0px; } + .iti-flag.bz { + height: 14px; + background-position: -812px 0px; } + .iti-flag.ca { + height: 10px; + background-position: -834px 0px; } + .iti-flag.cc { + height: 10px; + background-position: -856px 0px; } + .iti-flag.cd { + height: 15px; + background-position: -878px 0px; } + .iti-flag.cf { + height: 14px; + background-position: -900px 0px; } + .iti-flag.cg { + height: 14px; + background-position: -922px 0px; } + .iti-flag.ch { + height: 15px; + background-position: -944px 0px; } + .iti-flag.ci { + height: 14px; + background-position: -961px 0px; } + .iti-flag.ck { + height: 10px; + background-position: -983px 0px; } + .iti-flag.cl { + height: 14px; + background-position: -1005px 0px; } + .iti-flag.cm { + height: 14px; + background-position: -1027px 0px; } + .iti-flag.cn { + height: 14px; + background-position: -1049px 0px; } + .iti-flag.co { + height: 14px; + background-position: -1071px 0px; } + .iti-flag.cp { + height: 14px; + background-position: -1093px 0px; } + .iti-flag.cr { + height: 12px; + background-position: -1115px 0px; } + .iti-flag.cu { + height: 10px; + background-position: -1137px 0px; } + .iti-flag.cv { + height: 12px; + background-position: -1159px 0px; } + .iti-flag.cw { + height: 14px; + background-position: -1181px 0px; } + .iti-flag.cx { + height: 10px; + background-position: -1203px 0px; } + .iti-flag.cy { + height: 13px; + background-position: -1225px 0px; } + .iti-flag.cz { + height: 14px; + background-position: -1247px 0px; } + .iti-flag.de { + height: 12px; + background-position: -1269px 0px; } + .iti-flag.dg { + height: 10px; + background-position: -1291px 0px; } + .iti-flag.dj { + height: 14px; + background-position: -1313px 0px; } + .iti-flag.dk { + height: 15px; + background-position: -1335px 0px; } + .iti-flag.dm { + height: 10px; + background-position: -1357px 0px; } + .iti-flag.do { + height: 13px; + background-position: -1379px 0px; } + .iti-flag.dz { + height: 14px; + background-position: -1401px 0px; } + .iti-flag.ea { + height: 14px; + background-position: -1423px 0px; } + .iti-flag.ec { + height: 14px; + background-position: -1445px 0px; } + .iti-flag.ee { + height: 13px; + background-position: -1467px 0px; } + .iti-flag.eg { + height: 14px; + background-position: -1489px 0px; } + .iti-flag.eh { + height: 10px; + background-position: -1511px 0px; } + .iti-flag.er { + height: 10px; + background-position: -1533px 0px; } + .iti-flag.es { + height: 14px; + background-position: -1555px 0px; } + .iti-flag.et { + height: 10px; + background-position: -1577px 0px; } + .iti-flag.eu { + height: 14px; + background-position: -1599px 0px; } + .iti-flag.fi { + height: 12px; + background-position: -1621px 0px; } + .iti-flag.fj { + height: 10px; + background-position: -1643px 0px; } + .iti-flag.fk { + height: 10px; + background-position: -1665px 0px; } + .iti-flag.fm { + height: 11px; + background-position: -1687px 0px; } + .iti-flag.fo { + height: 15px; + background-position: -1709px 0px; } + .iti-flag.fr { + height: 14px; + background-position: -1731px 0px; } + .iti-flag.ga { + height: 15px; + background-position: -1753px 0px; } + .iti-flag.gb { + height: 10px; + background-position: -1775px 0px; } + .iti-flag.gd { + height: 12px; + background-position: -1797px 0px; } + .iti-flag.ge { + height: 14px; + background-position: -1819px 0px; } + .iti-flag.gf { + height: 14px; + background-position: -1841px 0px; } + .iti-flag.gg { + height: 14px; + background-position: -1863px 0px; } + .iti-flag.gh { + height: 14px; + background-position: -1885px 0px; } + .iti-flag.gi { + height: 10px; + background-position: -1907px 0px; } + .iti-flag.gl { + height: 14px; + background-position: -1929px 0px; } + .iti-flag.gm { + height: 14px; + background-position: -1951px 0px; } + .iti-flag.gn { + height: 14px; + background-position: -1973px 0px; } + .iti-flag.gp { + height: 14px; + background-position: -1995px 0px; } + .iti-flag.gq { + height: 14px; + background-position: -2017px 0px; } + .iti-flag.gr { + height: 14px; + background-position: -2039px 0px; } + .iti-flag.gs { + height: 10px; + background-position: -2061px 0px; } + .iti-flag.gt { + height: 13px; + background-position: -2083px 0px; } + .iti-flag.gu { + height: 11px; + background-position: -2105px 0px; } + .iti-flag.gw { + height: 10px; + background-position: -2127px 0px; } + .iti-flag.gy { + height: 12px; + background-position: -2149px 0px; } + .iti-flag.hk { + height: 14px; + background-position: -2171px 0px; } + .iti-flag.hm { + height: 10px; + background-position: -2193px 0px; } + .iti-flag.hn { + height: 10px; + background-position: -2215px 0px; } + .iti-flag.hr { + height: 10px; + background-position: -2237px 0px; } + .iti-flag.ht { + height: 12px; + background-position: -2259px 0px; } + .iti-flag.hu { + height: 10px; + background-position: -2281px 0px; } + .iti-flag.ic { + height: 14px; + background-position: -2303px 0px; } + .iti-flag.id { + height: 14px; + background-position: -2325px 0px; } + .iti-flag.ie { + height: 10px; + background-position: -2347px 0px; } + .iti-flag.il { + height: 15px; + background-position: -2369px 0px; } + .iti-flag.im { + height: 10px; + background-position: -2391px 0px; } + .iti-flag.in { + height: 14px; + background-position: -2413px 0px; } + .iti-flag.io { + height: 10px; + background-position: -2435px 0px; } + .iti-flag.iq { + height: 14px; + background-position: -2457px 0px; } + .iti-flag.ir { + height: 12px; + background-position: -2479px 0px; } + .iti-flag.is { + height: 15px; + background-position: -2501px 0px; } + .iti-flag.it { + height: 14px; + background-position: -2523px 0px; } + .iti-flag.je { + height: 12px; + background-position: -2545px 0px; } + .iti-flag.jm { + height: 10px; + background-position: -2567px 0px; } + .iti-flag.jo { + height: 10px; + background-position: -2589px 0px; } + .iti-flag.jp { + height: 14px; + background-position: -2611px 0px; } + .iti-flag.ke { + height: 14px; + background-position: -2633px 0px; } + .iti-flag.kg { + height: 12px; + background-position: -2655px 0px; } + .iti-flag.kh { + height: 13px; + background-position: -2677px 0px; } + .iti-flag.ki { + height: 10px; + background-position: -2699px 0px; } + .iti-flag.km { + height: 12px; + background-position: -2721px 0px; } + .iti-flag.kn { + height: 14px; + background-position: -2743px 0px; } + .iti-flag.kp { + height: 10px; + background-position: -2765px 0px; } + .iti-flag.kr { + height: 14px; + background-position: -2787px 0px; } + .iti-flag.kw { + height: 10px; + background-position: -2809px 0px; } + .iti-flag.ky { + height: 10px; + background-position: -2831px 0px; } + .iti-flag.kz { + height: 10px; + background-position: -2853px 0px; } + .iti-flag.la { + height: 14px; + background-position: -2875px 0px; } + .iti-flag.lb { + height: 14px; + background-position: -2897px 0px; } + .iti-flag.lc { + height: 10px; + background-position: -2919px 0px; } + .iti-flag.li { + height: 12px; + background-position: -2941px 0px; } + .iti-flag.lk { + height: 10px; + background-position: -2963px 0px; } + .iti-flag.lr { + height: 11px; + background-position: -2985px 0px; } + .iti-flag.ls { + height: 14px; + background-position: -3007px 0px; } + .iti-flag.lt { + height: 12px; + background-position: -3029px 0px; } + .iti-flag.lu { + height: 12px; + background-position: -3051px 0px; } + .iti-flag.lv { + height: 10px; + background-position: -3073px 0px; } + .iti-flag.ly { + height: 10px; + background-position: -3095px 0px; } + .iti-flag.ma { + height: 14px; + background-position: -3117px 0px; } + .iti-flag.mc { + height: 15px; + background-position: -3139px 0px; } + .iti-flag.md { + height: 10px; + background-position: -3160px 0px; } + .iti-flag.me { + height: 10px; + background-position: -3182px 0px; } + .iti-flag.mf { + height: 14px; + background-position: -3204px 0px; } + .iti-flag.mg { + height: 14px; + background-position: -3226px 0px; } + .iti-flag.mh { + height: 11px; + background-position: -3248px 0px; } + .iti-flag.mk { + height: 10px; + background-position: -3270px 0px; } + .iti-flag.ml { + height: 14px; + background-position: -3292px 0px; } + .iti-flag.mm { + height: 14px; + background-position: -3314px 0px; } + .iti-flag.mn { + height: 10px; + background-position: -3336px 0px; } + .iti-flag.mo { + height: 14px; + background-position: -3358px 0px; } + .iti-flag.mp { + height: 10px; + background-position: -3380px 0px; } + .iti-flag.mq { + height: 14px; + background-position: -3402px 0px; } + .iti-flag.mr { + height: 14px; + background-position: -3424px 0px; } + .iti-flag.ms { + height: 10px; + background-position: -3446px 0px; } + .iti-flag.mt { + height: 14px; + background-position: -3468px 0px; } + .iti-flag.mu { + height: 14px; + background-position: -3490px 0px; } + .iti-flag.mv { + height: 14px; + background-position: -3512px 0px; } + .iti-flag.mw { + height: 14px; + background-position: -3534px 0px; } + .iti-flag.mx { + height: 12px; + background-position: -3556px 0px; } + .iti-flag.my { + height: 10px; + background-position: -3578px 0px; } + .iti-flag.mz { + height: 14px; + background-position: -3600px 0px; } + .iti-flag.na { + height: 14px; + background-position: -3622px 0px; } + .iti-flag.nc { + height: 10px; + background-position: -3644px 0px; } + .iti-flag.ne { + height: 15px; + background-position: -3666px 0px; } + .iti-flag.nf { + height: 10px; + background-position: -3686px 0px; } + .iti-flag.ng { + height: 10px; + background-position: -3708px 0px; } + .iti-flag.ni { + height: 12px; + background-position: -3730px 0px; } + .iti-flag.nl { + height: 14px; + background-position: -3752px 0px; } + .iti-flag.no { + height: 15px; + background-position: -3774px 0px; } + .iti-flag.np { + height: 15px; + background-position: -3796px 0px; } + .iti-flag.nr { + height: 10px; + background-position: -3811px 0px; } + .iti-flag.nu { + height: 10px; + background-position: -3833px 0px; } + .iti-flag.nz { + height: 10px; + background-position: -3855px 0px; } + .iti-flag.om { + height: 10px; + background-position: -3877px 0px; } + .iti-flag.pa { + height: 14px; + background-position: -3899px 0px; } + .iti-flag.pe { + height: 14px; + background-position: -3921px 0px; } + .iti-flag.pf { + height: 14px; + background-position: -3943px 0px; } + .iti-flag.pg { + height: 15px; + background-position: -3965px 0px; } + .iti-flag.ph { + height: 10px; + background-position: -3987px 0px; } + .iti-flag.pk { + height: 14px; + background-position: -4009px 0px; } + .iti-flag.pl { + height: 13px; + background-position: -4031px 0px; } + .iti-flag.pm { + height: 14px; + background-position: -4053px 0px; } + .iti-flag.pn { + height: 10px; + background-position: -4075px 0px; } + .iti-flag.pr { + height: 14px; + background-position: -4097px 0px; } + .iti-flag.ps { + height: 10px; + background-position: -4119px 0px; } + .iti-flag.pt { + height: 14px; + background-position: -4141px 0px; } + .iti-flag.pw { + height: 13px; + background-position: -4163px 0px; } + .iti-flag.py { + height: 11px; + background-position: -4185px 0px; } + .iti-flag.qa { + height: 8px; + background-position: -4207px 0px; } + .iti-flag.re { + height: 14px; + background-position: -4229px 0px; } + .iti-flag.ro { + height: 14px; + background-position: -4251px 0px; } + .iti-flag.rs { + height: 14px; + background-position: -4273px 0px; } + .iti-flag.ru { + height: 14px; + background-position: -4295px 0px; } + .iti-flag.rw { + height: 14px; + background-position: -4317px 0px; } + .iti-flag.sa { + height: 14px; + background-position: -4339px 0px; } + .iti-flag.sb { + height: 10px; + background-position: -4361px 0px; } + .iti-flag.sc { + height: 10px; + background-position: -4383px 0px; } + .iti-flag.sd { + height: 10px; + background-position: -4405px 0px; } + .iti-flag.se { + height: 13px; + background-position: -4427px 0px; } + .iti-flag.sg { + height: 14px; + background-position: -4449px 0px; } + .iti-flag.sh { + height: 10px; + background-position: -4471px 0px; } + .iti-flag.si { + height: 10px; + background-position: -4493px 0px; } + .iti-flag.sj { + height: 15px; + background-position: -4515px 0px; } + .iti-flag.sk { + height: 14px; + background-position: -4537px 0px; } + .iti-flag.sl { + height: 14px; + background-position: -4559px 0px; } + .iti-flag.sm { + height: 15px; + background-position: -4581px 0px; } + .iti-flag.sn { + height: 14px; + background-position: -4603px 0px; } + .iti-flag.so { + height: 14px; + background-position: -4625px 0px; } + .iti-flag.sr { + height: 14px; + background-position: -4647px 0px; } + .iti-flag.ss { + height: 10px; + background-position: -4669px 0px; } + .iti-flag.st { + height: 10px; + background-position: -4691px 0px; } + .iti-flag.sv { + height: 12px; + background-position: -4713px 0px; } + .iti-flag.sx { + height: 14px; + background-position: -4735px 0px; } + .iti-flag.sy { + height: 14px; + background-position: -4757px 0px; } + .iti-flag.sz { + height: 14px; + background-position: -4779px 0px; } + .iti-flag.ta { + height: 10px; + background-position: -4801px 0px; } + .iti-flag.tc { + height: 10px; + background-position: -4823px 0px; } + .iti-flag.td { + height: 14px; + background-position: -4845px 0px; } + .iti-flag.tf { + height: 14px; + background-position: -4867px 0px; } + .iti-flag.tg { + height: 13px; + background-position: -4889px 0px; } + .iti-flag.th { + height: 14px; + background-position: -4911px 0px; } + .iti-flag.tj { + height: 10px; + background-position: -4933px 0px; } + .iti-flag.tk { + height: 10px; + background-position: -4955px 0px; } + .iti-flag.tl { + height: 10px; + background-position: -4977px 0px; } + .iti-flag.tm { + height: 14px; + background-position: -4999px 0px; } + .iti-flag.tn { + height: 14px; + background-position: -5021px 0px; } + .iti-flag.to { + height: 10px; + background-position: -5043px 0px; } + .iti-flag.tr { + height: 14px; + background-position: -5065px 0px; } + .iti-flag.tt { + height: 12px; + background-position: -5087px 0px; } + .iti-flag.tv { + height: 10px; + background-position: -5109px 0px; } + .iti-flag.tw { + height: 14px; + background-position: -5131px 0px; } + .iti-flag.tz { + height: 14px; + background-position: -5153px 0px; } + .iti-flag.ua { + height: 14px; + background-position: -5175px 0px; } + .iti-flag.ug { + height: 14px; + background-position: -5197px 0px; } + .iti-flag.um { + height: 11px; + background-position: -5219px 0px; } + .iti-flag.us { + height: 11px; + background-position: -5241px 0px; } + .iti-flag.uy { + height: 14px; + background-position: -5263px 0px; } + .iti-flag.uz { + height: 10px; + background-position: -5285px 0px; } + .iti-flag.va { + height: 15px; + background-position: -5307px 0px; } + .iti-flag.vc { + height: 14px; + background-position: -5324px 0px; } + .iti-flag.ve { + height: 14px; + background-position: -5346px 0px; } + .iti-flag.vg { + height: 10px; + background-position: -5368px 0px; } + .iti-flag.vi { + height: 14px; + background-position: -5390px 0px; } + .iti-flag.vn { + height: 14px; + background-position: -5412px 0px; } + .iti-flag.vu { + height: 12px; + background-position: -5434px 0px; } + .iti-flag.wf { + height: 14px; + background-position: -5456px 0px; } + .iti-flag.ws { + height: 10px; + background-position: -5478px 0px; } + .iti-flag.xk { + height: 15px; + background-position: -5500px 0px; } + .iti-flag.ye { + height: 14px; + background-position: -5522px 0px; } + .iti-flag.yt { + height: 14px; + background-position: -5544px 0px; } + .iti-flag.za { + height: 14px; + background-position: -5566px 0px; } + .iti-flag.zm { + height: 14px; + background-position: -5588px 0px; } + .iti-flag.zw { + height: 10px; + background-position: -5610px 0px; } + +.iti-flag { + width: 20px; + height: 15px; + box-shadow: 0px 0px 1px 0px #888; + background-image: url("../img/flags.png"); + background-repeat: no-repeat; + background-color: #DBDBDB; + background-position: 20px 0; } + @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2 / 1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { + .iti-flag { + background-image: url("../img/flags@2x.png"); } } + +.iti-flag.np { + background-color: transparent; } diff --git a/dist/less/avUi/change-lang-directive/change-lang-directive.less b/dist/less/avUi/change-lang-directive/change-lang-directive.less index 022daf87..87199e02 100644 --- a/dist/less/avUi/change-lang-directive/change-lang-directive.less +++ b/dist/less/avUi/change-lang-directive/change-lang-directive.less @@ -3,7 +3,7 @@ padding-left: 8px; &:hover { - background-color: @av-primary-contrast; + background-color: lightgray; } > a:hover { diff --git a/dist/less/avUi/documentation-directive/documentation-directive.less b/dist/less/avUi/documentation-directive/documentation-directive.less new file mode 100644 index 00000000..ad46e07b --- /dev/null +++ b/dist/less/avUi/documentation-directive/documentation-directive.less @@ -0,0 +1,6 @@ + +[documentation-directive] { + .docu-ul { + margin-bottom: 0px; + } +} diff --git a/dist/less/avUi/foot-directive/foot-directive.less b/dist/less/avUi/foot-directive/foot-directive.less index fddc978d..d62dee79 100644 --- a/dist/less/avUi/foot-directive/foot-directive.less +++ b/dist/less/avUi/foot-directive/foot-directive.less @@ -1,14 +1,20 @@ -.commonfoot { - padding: 40px 0 200px 0; - background-color: @av-bg; -} +[av-foot] { + .commonfoot { + padding: 40px 0 200px 0; + background-color: @av-bg; + } -.commonfoot ul { - margin: 0px; - padding: 0px; - list-style: none; -} + .commonfoot ul { + margin: 0px; + padding: 0px; + list-style: none; + } -.commonfoot .social { - margin-top: 10px; -} + .commonfoot .social { + margin-top: 10px; + } + + .powered-by { + margin-left: 50px; + } +} \ No newline at end of file diff --git a/dist/less/avUi/global.less b/dist/less/avUi/global.less index b602f4d6..f7e19a9b 100644 --- a/dist/less/avUi/global.less +++ b/dist/less/avUi/global.less @@ -19,12 +19,16 @@ a:hover, a:active { border: 0; } +.modal-text-success { + color: #3c763d; +} + .text-brand-success { color: @brand-success; } .text-brand-warning { - color: @brand-warning; + color: #4a4a4a; } .text-brand-danger { @@ -147,4 +151,4 @@ a:hover, a:active { -moz-user-select: none; -ms-user-select: none; user-select: none; -} \ No newline at end of file +} diff --git a/dist/libCommon-v17.04.js b/dist/libCommon-v20.2.0.js similarity index 57% rename from dist/libCommon-v17.04.js rename to dist/libCommon-v20.2.0.js index 532fc1c8..1c149707 100644 --- a/dist/libCommon-v17.04.js +++ b/dist/libCommon-v20.2.0.js @@ -1,52 +1,50 @@ +if ("undefined" == typeof jQuery) throw new Error("Bootstrap's JavaScript requires jQuery"); + function uiUploader($log) { "use strict"; - function addFiles(files) { - for (var i = 0; i < files.length; i++) self.files.push(files[i]); - } - function getFiles() { - return self.files; - } + var self = this; function startUpload(options) { self.options = options; for (var i = 0; i < self.files.length && self.activeUploads != self.options.concurrency; i++) self.files[i].active || ajaxUpload(self.files[i], self.options.url); } - function removeFile(file) { - self.files.splice(self.files.indexOf(file), 1); - } - function removeAll() { - self.files.splice(0, self.files.length); - } - function getHumanSize(bytes) { - var sizes = [ "n/a", "bytes", "KiB", "MiB", "GiB", "TB", "PB", "EiB", "ZiB", "YiB" ], i = +Math.floor(Math.log(bytes) / Math.log(1024)); - return (bytes / Math.pow(1024, i)).toFixed(i ? 1 : 0) + " " + sizes[isNaN(bytes) ? 0 : i + 1]; - } + return self.files = [], self.options = {}, self.activeUploads = 0, $log.info("uiUploader loaded"), + { + addFiles: function(files) { + for (var i = 0; i < files.length; i++) self.files.push(files[i]); + }, + getFiles: function() { + return self.files; + }, + files: self.files, + startUpload: startUpload, + removeFile: function(file) { + self.files.splice(self.files.indexOf(file), 1); + }, + removeAll: function() { + self.files.splice(0, self.files.length); + } + }; function ajaxUpload(file, url) { - var xhr, formData, prop, data = "", key = "file"; - if (self.activeUploads += 1, file.active = !0, xhr = new window.XMLHttpRequest(), + var xhr, formData; + return self.activeUploads += 1, file.active = !0, xhr = new window.XMLHttpRequest(), formData = new window.FormData(), xhr.open("POST", url), xhr.upload.onloadstart = function() {}, xhr.upload.onprogress = function(event) { - event.lengthComputable && (file.loaded = event.loaded, file.humanSize = getHumanSize(event.loaded), + var bytes, i; + event.lengthComputable && (file.loaded = event.loaded, file.humanSize = (bytes = event.loaded, + i = +Math.floor(Math.log(bytes) / Math.log(1024)), (bytes / Math.pow(1024, i)).toFixed(i ? 1 : 0) + " " + [ "n/a", "bytes", "KiB", "MiB", "GiB", "TB", "PB", "EiB", "ZiB", "YiB" ][isNaN(bytes) ? 0 : 1 + i]), self.options.onProgress(file)); }, xhr.onload = function() { - self.activeUploads -= 1, startUpload(self.options), self.options.onCompleted(file, xhr.responseText); - }, xhr.onerror = function() {}, data) for (prop in data) data.hasOwnProperty(prop) && formData.append(prop, data[prop]); - return formData.append(key, file, file.name), xhr.send(formData), xhr; + --self.activeUploads, startUpload(self.options), self.options.onCompleted(file, xhr.responseText); + }, xhr.onerror = function() {}, formData.append("file", file, file.name), xhr.send(formData), + xhr; } - var self = this; - return self.files = [], self.options = {}, self.activeUploads = 0, $log.info("uiUploader loaded"), - { - addFiles: addFiles, - getFiles: getFiles, - files: self.files, - startUpload: startUpload, - removeFile: removeFile, - removeAll: removeAll - }; } +var mod; + function RC4(seed) { - this.s = new Array(256), this.i = 0, this.j = 0; - for (var i = 0; i < 256; i++) this.s[i] = i; + this.s = new Array(256), this.i = 0; + for (var i = this.j = 0; i < 256; i++) this.s[i] = i; seed && this.mix(seed); } @@ -62,90 +60,56 @@ function q(a) { throw a; } -function y(a, b, c) { - 4 !== b.length && q(new sjcl.exception.invalid("invalid aes block size")); - var d = a.a[c], e = b[0] ^ d[0], f = b[c ? 3 : 1] ^ d[1], g = b[2] ^ d[2]; - b = b[c ? 1 : 3] ^ d[3]; - var h, l, k, m, n = d.length / 4 - 2, p = 4, s = [ 0, 0, 0, 0 ]; - h = a.j[c], a = h[0]; - var r = h[1], v = h[2], w = h[3], x = h[4]; - for (m = 0; m < n; m++) h = a[e >>> 24] ^ r[f >> 16 & 255] ^ v[g >> 8 & 255] ^ w[255 & b] ^ d[p], - l = a[f >>> 24] ^ r[g >> 16 & 255] ^ v[b >> 8 & 255] ^ w[255 & e] ^ d[p + 1], k = a[g >>> 24] ^ r[b >> 16 & 255] ^ v[e >> 8 & 255] ^ w[255 & f] ^ d[p + 2], - b = a[b >>> 24] ^ r[e >> 16 & 255] ^ v[f >> 8 & 255] ^ w[255 & g] ^ d[p + 3], p += 4, - e = h, f = l, g = k; - for (m = 0; 4 > m; m++) s[c ? 3 & -m : m] = x[e >>> 24] << 24 ^ x[f >> 16 & 255] << 16 ^ x[g >> 8 & 255] << 8 ^ x[255 & b] ^ d[p++], - h = e, e = f, f = g, g = b, b = h; - return s; -} - -function z(a, b) { - var c, d, e, f = b.slice(0), g = a.q, h = a.a, l = g[0], k = g[1], n = g[2], m = g[3], p = g[4], s = g[5], r = g[6], v = g[7]; - for (c = 0; 64 > c; c++) 16 > c ? d = f[c] : (d = f[c + 1 & 15], e = f[c + 14 & 15], - d = f[15 & c] = (d >>> 7 ^ d >>> 18 ^ d >>> 3 ^ d << 25 ^ d << 14) + (e >>> 17 ^ e >>> 19 ^ e >>> 10 ^ e << 15 ^ e << 13) + f[15 & c] + f[c + 9 & 15] | 0), - d = d + v + (p >>> 6 ^ p >>> 11 ^ p >>> 25 ^ p << 26 ^ p << 21 ^ p << 7) + (r ^ p & (s ^ r)) + h[c], - v = r, r = s, s = p, p = m + d | 0, m = n, n = k, k = l, l = d + (k & n ^ m & (k ^ n)) + (k >>> 2 ^ k >>> 13 ^ k >>> 22 ^ k << 30 ^ k << 19 ^ k << 10) | 0; - g[0] = g[0] + l | 0, g[1] = g[1] + k | 0, g[2] = g[2] + n | 0, g[3] = g[3] + m | 0, - g[4] = g[4] + p | 0, g[5] = g[5] + s | 0, g[6] = g[6] + r | 0, g[7] = g[7] + v | 0; -} - -function C(a, b) { - var c, d = sjcl.random.z[a], e = []; - for (c in d) d.hasOwnProperty(c) && e.push(d[c]); - for (c = 0; c < e.length; c++) e[c](b); -} - -function A(a) { - a.a = B(a).concat(B(a)), a.A = new sjcl.cipher.aes(a.a); -} - -function B(a) { - for (var b = 0; 4 > b && (a.e[b] = a.e[b] + 1 | 0, !a.e[b]); b++) ; - return a.A.encrypt(a.e); -} - -if ("undefined" == typeof jQuery) throw new Error("Bootstrap's JavaScript requires jQuery"); - -+function($) { +!function() { + "use strict"; + var version = jQuery.fn.jquery.split(" ")[0].split("."); + if (version[0] < 2 && version[1] < 9 || 1 == version[0] && 9 == version[1] && version[2] < 1 || 3 < version[0]) throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4"); +}(), function($) { "use strict"; - function transitionEnd() { - var el = document.createElement("bootstrap"), transEndEventNames = { - WebkitTransition: "webkitTransitionEnd", - MozTransition: "transitionend", - OTransition: "oTransitionEnd otransitionend", - transition: "transitionend" - }; - for (var name in transEndEventNames) if (void 0 !== el.style[name]) return { - end: transEndEventNames[name] - }; - return !1; - } $.fn.emulateTransitionEnd = function(duration) { var called = !1, $el = this; - $(this).one($.support.transition.end, function() { + $(this).one("bsTransitionEnd", function() { called = !0; }); - var callback = function() { + return setTimeout(function() { called || $($el).trigger($.support.transition.end); - }; - return setTimeout(callback, duration), this; + }, duration), this; }, $(function() { - $.support.transition = transitionEnd(); + $.support.transition = function() { + var el = document.createElement("bootstrap"), transEndEventNames = { + WebkitTransition: "webkitTransitionEnd", + MozTransition: "transitionend", + OTransition: "oTransitionEnd otransitionend", + transition: "transitionend" + }; + for (var name in transEndEventNames) if (void 0 !== el.style[name]) return { + end: transEndEventNames[name] + }; + return !1; + }(), $.support.transition && ($.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function(e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments); + } + }); }); }(jQuery), function($) { "use strict"; - var dismiss = '[data-dismiss="alert"]', Alert = function(el) { + function Alert(el) { $(el).on("click", dismiss, this.close); - }; - Alert.prototype.close = function(e) { + } + var dismiss = '[data-dismiss="alert"]'; + Alert.VERSION = "3.4.1", Alert.TRANSITION_DURATION = 150, Alert.prototype.close = function(e) { + var $this = $(this), selector = $this.attr("data-target"); + selector = "#" === (selector = selector || (selector = $this.attr("href")) && selector.replace(/.*(?=#[^\s]*$)/, "")) ? [] : selector; + var $parent = $(document).find(selector); function removeElement() { - $parent.trigger("closed.bs.alert").remove(); + $parent.detach().trigger("closed.bs.alert").remove(); } - var $this = $(this), selector = $this.attr("data-target"); - selector || (selector = $this.attr("href"), selector = selector && selector.replace(/.*(?=#[^\s]*$)/, "")); - var $parent = $(selector); - e && e.preventDefault(), $parent.length || ($parent = $this.hasClass("alert") ? $this : $this.parent()), + e && e.preventDefault(), $parent.length || ($parent = $this.closest(".alert")), $parent.trigger(e = $.Event("close.bs.alert")), e.isDefaultPrevented() || ($parent.removeClass("in"), - $.support.transition && $parent.hasClass("fade") ? $parent.one($.support.transition.end, removeElement).emulateTransitionEnd(150) : removeElement()); + $.support.transition && $parent.hasClass("fade") ? $parent.one("bsTransitionEnd", removeElement).emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement()); }; var old = $.fn.alert; $.fn.alert = function(option) { @@ -162,58 +126,91 @@ if ("undefined" == typeof jQuery) throw new Error("Bootstrap's JavaScript requir this.$element = $(element), this.options = $.extend({}, Button.DEFAULTS, options), this.isLoading = !1; }; - Button.DEFAULTS = { + function Plugin(option) { + return this.each(function() { + var $this = $(this), data = $this.data("bs.button"), options = "object" == typeof option && option; + data || $this.data("bs.button", data = new Button(this, options)), "toggle" == option ? data.toggle() : option && data.setState(option); + }); + } + Button.VERSION = "3.4.1", Button.DEFAULTS = { loadingText: "loading..." }, Button.prototype.setState = function(state) { var d = "disabled", $el = this.$element, val = $el.is("input") ? "val" : "html", data = $el.data(); - state += "Text", data.resetText || $el.data("resetText", $el[val]()), $el[val](data[state] || this.options[state]), - setTimeout($.proxy(function() { - "loadingText" == state ? (this.isLoading = !0, $el.addClass(d).attr(d, d)) : this.isLoading && (this.isLoading = !1, - $el.removeClass(d).removeAttr(d)); + state += "Text", null == data.resetText && $el.data("resetText", $el[val]()), setTimeout($.proxy(function() { + $el[val](null == data[state] ? this.options[state] : data[state]), "loadingText" == state ? (this.isLoading = !0, + $el.addClass(d).attr(d, d).prop(d, !0)) : this.isLoading && (this.isLoading = !1, + $el.removeClass(d).removeAttr(d).prop(d, !1)); }, this), 0); }, Button.prototype.toggle = function() { var changed = !0, $parent = this.$element.closest('[data-toggle="buttons"]'); if ($parent.length) { var $input = this.$element.find("input"); - "radio" == $input.prop("type") && ($input.prop("checked") && this.$element.hasClass("active") ? changed = !1 : $parent.find(".active").removeClass("active")), - changed && $input.prop("checked", !this.$element.hasClass("active")).trigger("change"); - } - changed && this.$element.toggleClass("active"); + "radio" == $input.prop("type") ? ($input.prop("checked") && (changed = !1), $parent.find(".active").removeClass("active"), + this.$element.addClass("active")) : "checkbox" == $input.prop("type") && ($input.prop("checked") !== this.$element.hasClass("active") && (changed = !1), + this.$element.toggleClass("active")), $input.prop("checked", this.$element.hasClass("active")), + changed && $input.trigger("change"); + } else this.$element.attr("aria-pressed", !this.$element.hasClass("active")), this.$element.toggleClass("active"); }; var old = $.fn.button; - $.fn.button = function(option) { - return this.each(function() { - var $this = $(this), data = $this.data("bs.button"), options = "object" == typeof option && option; - data || $this.data("bs.button", data = new Button(this, options)), "toggle" == option ? data.toggle() : option && data.setState(option); - }); - }, $.fn.button.Constructor = Button, $.fn.button.noConflict = function() { + $.fn.button = Plugin, $.fn.button.Constructor = Button, $.fn.button.noConflict = function() { return $.fn.button = old, this; - }, $(document).on("click.bs.button.data-api", "[data-toggle^=button]", function(e) { - var $btn = $(e.target); - $btn.hasClass("btn") || ($btn = $btn.closest(".btn")), $btn.button("toggle"), e.preventDefault(); + }, $(document).on("click.bs.button.data-api", '[data-toggle^="button"]', function(e) { + var $btn = $(e.target).closest(".btn"); + Plugin.call($btn, "toggle"), $(e.target).is('input[type="radio"], input[type="checkbox"]') || (e.preventDefault(), + $btn.is("input,button") ? $btn.trigger("focus") : $btn.find("input:visible,button:visible").first().trigger("focus")); + }).on("focus.bs.button.data-api blur.bs.button.data-api", '[data-toggle^="button"]', function(e) { + $(e.target).closest(".btn").toggleClass("focus", /^focus(in)?$/.test(e.type)); }); }(jQuery), function($) { "use strict"; - var Carousel = function(element, options) { + function Carousel(element, options) { this.$element = $(element), this.$indicators = this.$element.find(".carousel-indicators"), - this.options = options, this.paused = this.sliding = this.interval = this.$active = this.$items = null, - "hover" == this.options.pause && this.$element.on("mouseenter", $.proxy(this.pause, this)).on("mouseleave", $.proxy(this.cycle, this)); - }; - Carousel.DEFAULTS = { + this.options = options, this.paused = null, this.sliding = null, this.interval = null, + this.$active = null, this.$items = null, this.options.keyboard && this.$element.on("keydown.bs.carousel", $.proxy(this.keydown, this)), + "hover" != this.options.pause || "ontouchstart" in document.documentElement || this.$element.on("mouseenter.bs.carousel", $.proxy(this.pause, this)).on("mouseleave.bs.carousel", $.proxy(this.cycle, this)); + } + function Plugin(option) { + return this.each(function() { + var $this = $(this), data = $this.data("bs.carousel"), options = $.extend({}, Carousel.DEFAULTS, $this.data(), "object" == typeof option && option), action = "string" == typeof option ? option : options.slide; + data || $this.data("bs.carousel", data = new Carousel(this, options)), "number" == typeof option ? data.to(option) : action ? data[action]() : options.interval && data.pause().cycle(); + }); + } + Carousel.VERSION = "3.4.1", Carousel.TRANSITION_DURATION = 600, Carousel.DEFAULTS = { interval: 5e3, pause: "hover", - wrap: !0 + wrap: !0, + keyboard: !0 + }, Carousel.prototype.keydown = function(e) { + if (!/input|textarea/i.test(e.target.tagName)) { + switch (e.which) { + case 37: + this.prev(); + break; + + case 39: + this.next(); + break; + + default: + return; + } + e.preventDefault(); + } }, Carousel.prototype.cycle = function(e) { return e || (this.paused = !1), this.interval && clearInterval(this.interval), this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)), this; - }, Carousel.prototype.getActiveIndex = function() { - return this.$active = this.$element.find(".item.active"), this.$items = this.$active.parent().children(), - this.$items.index(this.$active); + }, Carousel.prototype.getItemIndex = function(item) { + return this.$items = item.parent().children(".item"), this.$items.index(item || this.$active); + }, Carousel.prototype.getItemForDirection = function(direction, active) { + var activeIndex = this.getItemIndex(active); + if (("prev" == direction && 0 === activeIndex || "next" == direction && activeIndex == this.$items.length - 1) && !this.options.wrap) return active; + var itemIndex = (activeIndex + ("prev" == direction ? -1 : 1)) % this.$items.length; + return this.$items.eq(itemIndex); }, Carousel.prototype.to = function(pos) { - var that = this, activeIndex = this.getActiveIndex(); + var that = this, activeIndex = this.getItemIndex(this.$active = this.$element.find(".item.active")); if (!(pos > this.$items.length - 1 || pos < 0)) return this.sliding ? this.$element.one("slid.bs.carousel", function() { that.to(pos); - }) : activeIndex == pos ? this.pause().cycle() : this.slide(pos > activeIndex ? "next" : "prev", $(this.$items[pos])); + }) : activeIndex == pos ? this.pause().cycle() : this.slide(activeIndex < pos ? "next" : "prev", this.$items.eq(pos)); }, Carousel.prototype.pause = function(e) { return e || (this.paused = !0), this.$element.find(".next, .prev").length && $.support.transition && (this.$element.trigger($.support.transition.end), this.cycle(!0)), this.interval = clearInterval(this.interval), this; @@ -222,79 +219,96 @@ if ("undefined" == typeof jQuery) throw new Error("Bootstrap's JavaScript requir }, Carousel.prototype.prev = function() { if (!this.sliding) return this.slide("prev"); }, Carousel.prototype.slide = function(type, next) { - var $active = this.$element.find(".item.active"), $next = next || $active[type](), isCycling = this.interval, direction = "next" == type ? "left" : "right", fallback = "next" == type ? "first" : "last", that = this; - if (!$next.length) { - if (!this.options.wrap) return; - $next = this.$element.find(".item")[fallback](); - } + var $active = this.$element.find(".item.active"), $next = next || this.getItemForDirection(type, $active), isCycling = this.interval, direction = "next" == type ? "left" : "right", that = this; if ($next.hasClass("active")) return this.sliding = !1; - var e = $.Event("slide.bs.carousel", { - relatedTarget: $next[0], + var relatedTarget = $next[0], slideEvent = $.Event("slide.bs.carousel", { + relatedTarget: relatedTarget, direction: direction }); - return this.$element.trigger(e), e.isDefaultPrevented() ? void 0 : (this.sliding = !0, - isCycling && this.pause(), this.$indicators.length && (this.$indicators.find(".active").removeClass("active"), - this.$element.one("slid.bs.carousel", function() { - var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]); - $nextIndicator && $nextIndicator.addClass("active"); - })), $.support.transition && this.$element.hasClass("slide") ? ($next.addClass(type), - $next[0].offsetWidth, $active.addClass(direction), $next.addClass(direction), $active.one($.support.transition.end, function() { - $next.removeClass([ type, direction ].join(" ")).addClass("active"), $active.removeClass([ "active", direction ].join(" ")), - that.sliding = !1, setTimeout(function() { - that.$element.trigger("slid.bs.carousel"); - }, 0); - }).emulateTransitionEnd(1e3 * $active.css("transition-duration").slice(0, -1))) : ($active.removeClass("active"), - $next.addClass("active"), this.sliding = !1, this.$element.trigger("slid.bs.carousel")), - isCycling && this.cycle(), this); + if (this.$element.trigger(slideEvent), !slideEvent.isDefaultPrevented()) { + if (this.sliding = !0, isCycling && this.pause(), this.$indicators.length) { + this.$indicators.find(".active").removeClass("active"); + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]); + $nextIndicator && $nextIndicator.addClass("active"); + } + var slidEvent = $.Event("slid.bs.carousel", { + relatedTarget: relatedTarget, + direction: direction + }); + return $.support.transition && this.$element.hasClass("slide") ? ($next.addClass(type), + "object" == typeof $next && $next.length && $next[0].offsetWidth, $active.addClass(direction), + $next.addClass(direction), $active.one("bsTransitionEnd", function() { + $next.removeClass([ type, direction ].join(" ")).addClass("active"), $active.removeClass([ "active", direction ].join(" ")), + that.sliding = !1, setTimeout(function() { + that.$element.trigger(slidEvent); + }, 0); + }).emulateTransitionEnd(Carousel.TRANSITION_DURATION)) : ($active.removeClass("active"), + $next.addClass("active"), this.sliding = !1, this.$element.trigger(slidEvent)), + isCycling && this.cycle(), this; + } }; var old = $.fn.carousel; - $.fn.carousel = function(option) { - return this.each(function() { - var $this = $(this), data = $this.data("bs.carousel"), options = $.extend({}, Carousel.DEFAULTS, $this.data(), "object" == typeof option && option), action = "string" == typeof option ? option : options.slide; - data || $this.data("bs.carousel", data = new Carousel(this, options)), "number" == typeof option ? data.to(option) : action ? data[action]() : options.interval && data.pause().cycle(); - }); - }, $.fn.carousel.Constructor = Carousel, $.fn.carousel.noConflict = function() { + $.fn.carousel = Plugin, $.fn.carousel.Constructor = Carousel, $.fn.carousel.noConflict = function() { return $.fn.carousel = old, this; - }, $(document).on("click.bs.carousel.data-api", "[data-slide], [data-slide-to]", function(e) { - var href, $this = $(this), $target = $($this.attr("data-target") || (href = $this.attr("href")) && href.replace(/.*(?=#[^\s]+$)/, "")), options = $.extend({}, $target.data(), $this.data()), slideIndex = $this.attr("data-slide-to"); - slideIndex && (options.interval = !1), $target.carousel(options), (slideIndex = $this.attr("data-slide-to")) && $target.data("bs.carousel").to(slideIndex), - e.preventDefault(); - }), $(window).on("load", function() { + }; + function clickHandler(e) { + var $this = $(this), href = $this.attr("href"); + href = href && href.replace(/.*(?=#[^\s]+$)/, ""); + var target = $this.attr("data-target") || href, $target = $(document).find(target); + if ($target.hasClass("carousel")) { + var options = $.extend({}, $target.data(), $this.data()), slideIndex = $this.attr("data-slide-to"); + slideIndex && (options.interval = !1), Plugin.call($target, options), slideIndex && $target.data("bs.carousel").to(slideIndex), + e.preventDefault(); + } + } + $(document).on("click.bs.carousel.data-api", "[data-slide]", clickHandler).on("click.bs.carousel.data-api", "[data-slide-to]", clickHandler), + $(window).on("load", function() { $('[data-ride="carousel"]').each(function() { var $carousel = $(this); - $carousel.carousel($carousel.data()); + Plugin.call($carousel, $carousel.data()); }); }); }(jQuery), function($) { "use strict"; var Collapse = function(element, options) { this.$element = $(element), this.options = $.extend({}, Collapse.DEFAULTS, options), - this.transitioning = null, this.options.parent && (this.$parent = $(this.options.parent)), + this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],[data-toggle="collapse"][data-target="#' + element.id + '"]'), + this.transitioning = null, this.options.parent ? this.$parent = this.getParent() : this.addAriaAndCollapsedClass(this.$element, this.$trigger), this.options.toggle && this.toggle(); }; - Collapse.DEFAULTS = { + function getTargetFromTrigger($trigger) { + var href, target = $trigger.attr("data-target") || (href = $trigger.attr("href")) && href.replace(/.*(?=#[^\s]+$)/, ""); + return $(document).find(target); + } + function Plugin(option) { + return this.each(function() { + var $this = $(this), data = $this.data("bs.collapse"), options = $.extend({}, Collapse.DEFAULTS, $this.data(), "object" == typeof option && option); + !data && options.toggle && /show|hide/.test(option) && (options.toggle = !1), data || $this.data("bs.collapse", data = new Collapse(this, options)), + "string" == typeof option && data[option](); + }); + } + Collapse.VERSION = "3.4.1", Collapse.TRANSITION_DURATION = 350, Collapse.DEFAULTS = { toggle: !0 }, Collapse.prototype.dimension = function() { return this.$element.hasClass("width") ? "width" : "height"; }, Collapse.prototype.show = function() { if (!this.transitioning && !this.$element.hasClass("in")) { - var startEvent = $.Event("show.bs.collapse"); - if (this.$element.trigger(startEvent), !startEvent.isDefaultPrevented()) { - var actives = this.$parent && this.$parent.find("> .panel > .in"); - if (actives && actives.length) { - var hasData = actives.data("bs.collapse"); - if (hasData && hasData.transitioning) return; - actives.collapse("hide"), hasData || actives.data("bs.collapse", null); + var activesData, actives = this.$parent && this.$parent.children(".panel").children(".in, .collapsing"); + if (!(actives && actives.length && (activesData = actives.data("bs.collapse")) && activesData.transitioning)) { + var startEvent = $.Event("show.bs.collapse"); + if (this.$element.trigger(startEvent), !startEvent.isDefaultPrevented()) { + actives && actives.length && (Plugin.call(actives, "hide"), activesData || actives.data("bs.collapse", null)); + var dimension = this.dimension(); + this.$element.removeClass("collapse").addClass("collapsing")[dimension](0).attr("aria-expanded", !0), + this.$trigger.removeClass("collapsed").attr("aria-expanded", !0), this.transitioning = 1; + var complete = function() { + this.$element.removeClass("collapsing").addClass("collapse in")[dimension](""), + this.transitioning = 0, this.$element.trigger("shown.bs.collapse"); + }; + if (!$.support.transition) return complete.call(this); + var scrollSize = $.camelCase([ "scroll", dimension ].join("-")); + this.$element.one("bsTransitionEnd", $.proxy(complete, this)).emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]); } - var dimension = this.dimension(); - this.$element.removeClass("collapse").addClass("collapsing")[dimension](0), this.transitioning = 1; - var complete = function() { - this.$element.removeClass("collapsing").addClass("collapse in")[dimension]("auto"), - this.transitioning = 0, this.$element.trigger("shown.bs.collapse"); - }; - if (!$.support.transition) return complete.call(this); - var scrollSize = $.camelCase([ "scroll", dimension ].join("-")); - this.$element.one($.support.transition.end, $.proxy(complete, this)).emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]); } } }, Collapse.prototype.hide = function() { @@ -302,78 +316,80 @@ if ("undefined" == typeof jQuery) throw new Error("Bootstrap's JavaScript requir var startEvent = $.Event("hide.bs.collapse"); if (this.$element.trigger(startEvent), !startEvent.isDefaultPrevented()) { var dimension = this.dimension(); - this.$element[dimension](this.$element[dimension]())[0].offsetHeight, this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"), - this.transitioning = 1; + this.$element[dimension](this.$element[dimension]())[0].offsetHeight, this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded", !1), + this.$trigger.addClass("collapsed").attr("aria-expanded", !1), this.transitioning = 1; var complete = function() { - this.transitioning = 0, this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse"); + this.transitioning = 0, this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse"); }; if (!$.support.transition) return complete.call(this); - this.$element[dimension](0).one($.support.transition.end, $.proxy(complete, this)).emulateTransitionEnd(350); + this.$element[dimension](0).one("bsTransitionEnd", $.proxy(complete, this)).emulateTransitionEnd(Collapse.TRANSITION_DURATION); } } }, Collapse.prototype.toggle = function() { this[this.$element.hasClass("in") ? "hide" : "show"](); + }, Collapse.prototype.getParent = function() { + return $(document).find(this.options.parent).find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]').each($.proxy(function(i, element) { + var $element = $(element); + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element); + }, this)).end(); + }, Collapse.prototype.addAriaAndCollapsedClass = function($element, $trigger) { + var isOpen = $element.hasClass("in"); + $element.attr("aria-expanded", isOpen), $trigger.toggleClass("collapsed", !isOpen).attr("aria-expanded", isOpen); }; var old = $.fn.collapse; - $.fn.collapse = function(option) { - return this.each(function() { - var $this = $(this), data = $this.data("bs.collapse"), options = $.extend({}, Collapse.DEFAULTS, $this.data(), "object" == typeof option && option); - !data && options.toggle && "show" == option && (option = !option), data || $this.data("bs.collapse", data = new Collapse(this, options)), - "string" == typeof option && data[option](); - }); - }, $.fn.collapse.Constructor = Collapse, $.fn.collapse.noConflict = function() { + $.fn.collapse = Plugin, $.fn.collapse.Constructor = Collapse, $.fn.collapse.noConflict = function() { return $.fn.collapse = old, this; - }, $(document).on("click.bs.collapse.data-api", "[data-toggle=collapse]", function(e) { - var href, $this = $(this), target = $this.attr("data-target") || e.preventDefault() || (href = $this.attr("href")) && href.replace(/.*(?=#[^\s]+$)/, ""), $target = $(target), data = $target.data("bs.collapse"), option = data ? "toggle" : $this.data(), parent = $this.attr("data-parent"), $parent = parent && $(parent); - data && data.transitioning || ($parent && $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass("collapsed"), - $this[$target.hasClass("in") ? "addClass" : "removeClass"]("collapsed")), $target.collapse(option); + }, $(document).on("click.bs.collapse.data-api", '[data-toggle="collapse"]', function(e) { + var $this = $(this); + $this.attr("data-target") || e.preventDefault(); + var $target = getTargetFromTrigger($this), option = $target.data("bs.collapse") ? "toggle" : $this.data(); + Plugin.call($target, option); }); }(jQuery), function($) { "use strict"; - function clearMenus(e) { - $(backdrop).remove(), $(toggle).each(function() { - var $parent = getParent($(this)), relatedTarget = { - relatedTarget: this - }; - $parent.hasClass("open") && ($parent.trigger(e = $.Event("hide.bs.dropdown", relatedTarget)), - e.isDefaultPrevented() || $parent.removeClass("open").trigger("hidden.bs.dropdown", relatedTarget)); - }); + function Dropdown(element) { + $(element).on("click.bs.dropdown", this.toggle); } + var toggle = '[data-toggle="dropdown"]'; function getParent($this) { - var selector = $this.attr("data-target"); - selector || (selector = $this.attr("href"), selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, "")); - var $parent = selector && $(selector); + var selector = $this.attr("data-target"), $parent = "#" !== (selector = selector || (selector = $this.attr("href")) && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, "")) ? $(document).find(selector) : null; return $parent && $parent.length ? $parent : $this.parent(); } - var backdrop = ".dropdown-backdrop", toggle = "[data-toggle=dropdown]", Dropdown = function(element) { - $(element).on("click.bs.dropdown", this.toggle); - }; - Dropdown.prototype.toggle = function(e) { + function clearMenus(e) { + e && 3 === e.which || ($(".dropdown-backdrop").remove(), $(toggle).each(function() { + var $this = $(this), $parent = getParent($this), relatedTarget = { + relatedTarget: this + }; + $parent.hasClass("open") && (e && "click" == e.type && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target) || ($parent.trigger(e = $.Event("hide.bs.dropdown", relatedTarget)), + e.isDefaultPrevented() || ($this.attr("aria-expanded", "false"), $parent.removeClass("open").trigger($.Event("hidden.bs.dropdown", relatedTarget))))); + })); + } + Dropdown.VERSION = "3.4.1", Dropdown.prototype.toggle = function(e) { var $this = $(this); if (!$this.is(".disabled, :disabled")) { var $parent = getParent($this), isActive = $parent.hasClass("open"); if (clearMenus(), !isActive) { - "ontouchstart" in document.documentElement && !$parent.closest(".navbar-nav").length && $('