tellform/public/dist/form-application.js

1248 lines
36 KiB
JavaScript
Raw Normal View History

2016-06-20 22:35:41 +00:00
'use strict';
// Init the application configuration module for AngularJS application
var ApplicationConfiguration = (function() {
// Init module configuration options
2017-03-28 00:08:45 +00:00
var applicationModuleName = 'TellForm-Form';
2017-04-11 07:10:36 +00:00
var applicationModuleVendorDependencies = ['duScroll', 'ui.select', 'ngSanitize', 'vButton', 'ngResource', 'TellForm-Form.form_templates', 'ui.router', 'ui.bootstrap', 'pascalprecht.translate'];
2016-06-20 22:35:41 +00:00
// Add a new vertical module
var registerModule = function(moduleName, dependencies) {
// Create angular module
angular.module(moduleName, dependencies || []);
// Add the module to the AngularJS configuration file
angular.module(applicationModuleName).requires.push(moduleName);
};
return {
applicationModuleName: applicationModuleName,
applicationModuleVendorDependencies: applicationModuleVendorDependencies,
registerModule: registerModule
};
})();
'use strict';
//Start by defining the main module and adding the module dependencies
angular.module(ApplicationConfiguration.applicationModuleName, ApplicationConfiguration.applicationModuleVendorDependencies);
// Setting HTML5 Location Mode
angular.module(ApplicationConfiguration.applicationModuleName).config(['$locationProvider',
function($locationProvider) {
$locationProvider.hashPrefix('!');
}
]);
//Permission Constants
angular.module(ApplicationConfiguration.applicationModuleName).constant('APP_PERMISSIONS', {
2017-03-27 20:32:06 +00:00
viewAdminSettings: 'viewAdminSettings',
editAdminSettings: 'editAdminSettings',
editForm: 'editForm',
viewPrivateForm: 'viewPrivateForm'
2016-06-20 22:35:41 +00:00
});
//User Role constants
angular.module(ApplicationConfiguration.applicationModuleName).constant('USER_ROLES', {
2017-03-27 20:32:06 +00:00
admin: 'admin',
normal: 'user',
superuser: 'superuser'
2016-06-20 22:35:41 +00:00
});
//form url
angular.module(ApplicationConfiguration.applicationModuleName).constant('FORM_URL', '/forms/:formId');
//Then define the init function for starting up the application
angular.element(document).ready(function() {
//Fixing facebook bug with redirect
if (window.location.hash === '#_=_') window.location.hash = '#!';
//Then init the app
angular.bootstrap(document, [ApplicationConfiguration.applicationModuleName]);
});
'use strict';
// Use Application configuration module to register a new module
ApplicationConfiguration.registerModule('view-form', [
2017-04-11 07:10:36 +00:00
'ngFileUpload', 'ui.date', 'angular-input-stars'
2016-08-23 21:45:59 +00:00
]);
2016-06-20 22:35:41 +00:00
'use strict';
angular.module('view-form').config(['$translateProvider', function ($translateProvider) {
$translateProvider.translations('english', {
FORM_SUCCESS: 'Form entry successfully submitted!',
REVIEW: 'Review',
BACK_TO_FORM: 'Go back to Form',
EDIT_FORM: 'Edit this TellForm',
CREATE_FORM: 'Create this TellForm',
ADVANCEMENT: '{{done}} out of {{total}} answered',
CONTINUE_FORM: 'Continue to Form',
REQUIRED: 'required',
COMPLETING_NEEDED: '{{answers_not_completed}} answer(s) need completing',
OPTIONAL: 'optional',
ERROR_EMAIL_INVALID: 'Please enter a valid email address',
ERROR_NOT_A_NUMBER: 'Please enter valid numbers only',
ERROR_URL_INVALID: 'Please a valid url',
OK: 'OK',
ENTER: 'press ENTER',
YES: 'Yes',
NO: 'No',
NEWLINE: 'press SHIFT+ENTER to create a newline',
CONTINUE: 'Continue',
LEGAL_ACCEPT: 'I accept',
LEGAL_NO_ACCEPT: 'I dont accept',
DELETE: 'Delete',
CANCEL: 'Cancel',
SUBMIT: 'Submit',
UPLOAD_FILE: 'Upload your File',
});
$translateProvider.preferredLanguage('english')
.fallbackLanguage('english')
.useSanitizeValueStrategy('escape');
}]);
'use strict';
angular.module('view-form').config(['$translateProvider', function ($translateProvider) {
$translateProvider.translations('french', {
FORM_SUCCESS: 'Votre formulaire a été enregistré!',
REVIEW: 'Incomplet',
BACK_TO_FORM: 'Retourner au formulaire',
EDIT_FORM: 'Éditer le Tellform',
CREATE_FORM: 'Créer un TellForm',
ADVANCEMENT: '{{done}} complétés sur {{total}}',
CONTINUE_FORM: 'Aller au formulaire',
REQUIRED: 'obligatoire',
COMPLETING_NEEDED: '{{answers_not_completed}} réponse(s) doive(nt) être complétée(s)',
OPTIONAL: 'facultatif',
ERROR_EMAIL_INVALID: 'Merci de rentrer une adresse mail valide',
ERROR_NOT_A_NUMBER: 'Merce de ne rentrer que des nombres',
ERROR_URL_INVALID: 'Merci de rentrer une url valide',
OK: 'OK',
ENTER: 'presser ENTRÉE',
YES: 'Oui',
NO: 'Non',
NEWLINE: 'presser SHIFT+ENTER pour créer une nouvelle ligne',
CONTINUE: 'Continuer',
LEGAL_ACCEPT: 'Jaccepte',
LEGAL_NO_ACCEPT: 'Je naccepte pas',
DELETE: 'Supprimer',
CANCEL: 'Réinitialiser',
SUBMIT: 'Enregistrer',
UPLOAD_FILE: 'Envoyer un fichier',
Y: 'O',
N: 'N',
});
}]);
'use strict';
angular.module('view-form').config(['$translateProvider', function ($translateProvider) {
$translateProvider.translations('german', {
FORM_SUCCESS: 'Ihre Angaben wurden gespeichert.',
REVIEW: 'Unvollständig',
BACK_TO_FORM: 'Zurück zum Formular',
EDIT_FORM: '',
CREATE_FORM: '',
ADVANCEMENT: '{{done}} von {{total}} beantwortet',
CONTINUE_FORM: 'Zum Formular',
REQUIRED: 'verpflichtend',
COMPLETING_NEEDED: 'Es fehlen/fehtl noch {{answers_not_completed}} Antwort(en)',
OPTIONAL: 'fakultativ',
ERROR_EMAIL_INVALID: 'Bitte gültige Mailadresse eingeben',
ERROR_NOT_A_NUMBER: 'Bitte nur Zahlen eingeben',
ERROR_URL_INVALID: 'Bitte eine gültige URL eingeben',
OK: 'Okay',
ENTER: 'Eingabetaste drücken',
YES: 'Ja',
NO: 'Nein',
NEWLINE: 'Für eine neue Zeile SHIFT+ENTER drücken',
CONTINUE: 'Weiter',
LEGAL_ACCEPT: 'I accept',
LEGAL_NO_ACCEPT: 'I dont accept',
DELETE: 'Entfernen',
CANCEL: 'Canceln',
SUBMIT: 'Speichern',
UPLOAD_FILE: 'Datei versenden',
Y: 'J',
N: 'N',
});
}]);
'use strict';
angular.module('view-form').config(['$translateProvider', function ($translateProvider) {
$translateProvider.translations('italian', {
FORM_SUCCESS: 'Il formulario è stato inviato con successo!',
REVIEW: 'Incompleto',
BACK_TO_FORM: 'Ritorna al formulario',
EDIT_FORM: '',
CREATE_FORM: '',
ADVANCEMENT: '{{done}} su {{total}} completate',
CONTINUE_FORM: 'Vai al formulario',
REQUIRED: 'obbligatorio',
COMPLETING_NEEDED: '{{answers_not_completed}} risposta/e deve/ono essere completata/e',
OPTIONAL: 'opzionale',
ERROR_EMAIL_INVALID: 'Si prega di inserire un indirizzo email valido',
ERROR_NOT_A_NUMBER: 'Si prega di inserire solo numeri',
ERROR_URL_INVALID: 'Grazie per inserire un URL valido',
OK: 'OK',
ENTER: 'premere INVIO',
YES: 'Sì',
NO: 'No',
NEWLINE: 'premere SHIFT+INVIO per creare una nuova linea',
CONTINUE: 'Continua',
LEGAL_ACCEPT: 'I accept',
LEGAL_NO_ACCEPT: 'I dont accept',
DELETE: 'Cancella',
CANCEL: 'Reset',
SUBMIT: 'Registra',
UPLOAD_FILE: 'Invia un file',
Y: 'S',
N: 'N',
});
}]);
'use strict';
angular.module('view-form').config(['$translateProvider', function ($translateProvider) {
$translateProvider.translations('spanish', {
FORM_SUCCESS: '¡El formulario ha sido enviado con éxito!',
REVIEW: 'Revisar',
BACK_TO_FORM: 'Regresar al formulario',
EDIT_FORM: '',
CREATE_FORM: '',
ADVANCEMENT: '{{done}} de {{total}} contestadas',
CONTINUE_FORM: 'Continuar al formulario',
REQUIRED: 'Información requerida',
COMPLETING_NEEDED: '{{answers_not_completed}} respuesta(s) necesita(n) ser completada(s)',
OPTIONAL: 'Opcional',
ERROR_EMAIL_INVALID: 'Favor de proporcionar un correo electrónico válido',
ERROR_NOT_A_NUMBER: 'Por favor, introduzca sólo números válidos',
ERROR_URL_INVALID: 'Favor de proporcionar un url válido',
OK: 'OK',
ENTER: 'pulse INTRO',
YES: 'Si',
NO: 'No',
NEWLINE: 'presione SHIFT+INTRO para crear una nueva línea',
CONTINUE: 'Continuar',
2017-07-20 23:09:21 +00:00
LEGAL_ACCEPT: 'Yo acepto',
LEGAL_NO_ACCEPT: 'Yo no acepto',
2016-06-20 22:35:41 +00:00
DELETE: 'Eliminar',
CANCEL: 'Cancelar',
SUBMIT: 'Registrar',
UPLOAD_FILE: 'Cargar el archivo',
Y: 'S',
N: 'N'
});
}]);
'use strict';
// Setting up route
angular.module('view-form').config(['$stateProvider',
function($stateProvider) {
// Forms state routing
$stateProvider.
state('submitForm', {
url: '/forms/:formId',
templateUrl: '/static/form_modules/forms/base/views/submit-form.client.view.html',
resolve: {
Forms: 'Forms',
myForm: ["Forms", "$q", "$state", "$stateParams", function (Forms, $q, $state, $stateParams) {
var deferred = $q.defer();
console.log(Forms.get({formId: $stateParams.formId}).$promise);
return Forms.get({formId: $stateParams.formId}).$promise.then(function(data) {
console.log(data);
return data;
}, function(reason) {
console.log(reason);
$state.go('unauthorizedFormAccess');
return deferred.reject({redirectTo: 'unauthorizedFormAccess'});
});
//return Forms.get({formId: $stateParams.formId}).$promise;
2016-06-20 22:35:41 +00:00
}]
},
controller: 'SubmitFormController',
controllerAs: 'ctrl'
}).
state('unauthorizedFormAccess', {
url: '/forms/unauthorized',
templateUrl: '/static/form_modules/forms/base/views/unauthorized-form.client.view.html',
});
}
2016-06-20 22:35:41 +00:00
]);
(function () {
'use strict';
// Create the SendVisitorData service
angular
.module('view-form')
.factory('SendVisitorData', SendVisitorData);
SendVisitorData.$inject = ['Socket', '$state'];
function SendVisitorData(Socket, $state) {
// Create a controller method for sending visitor data
function send(form, lastActiveIndex, timeElapsed) {
2016-11-09 19:04:47 +00:00
var lang = window.navigator.userLanguage || window.navigator.language;
lang = lang.slice(0,2);
var userAgentString = navigator.userAgent;
var md = new MobileDetect(userAgentString);
var deviceType = 'other';
if (md.tablet()){
deviceType = 'tablet';
} else if (md.mobile()) {
deviceType = 'mobile';
2017-03-06 21:45:11 +00:00
} else if (!md.is('bot')) {
2016-11-09 19:04:47 +00:00
deviceType = 'desktop';
}
2016-11-09 19:20:50 +00:00
2017-07-20 23:09:21 +00:00
$.ajaxSetup( { 'async': false } );
2017-03-10 19:26:07 +00:00
var geoData = $.getJSON('https://freegeoip.net/json/').responseJSON;
2017-07-20 23:09:21 +00:00
$.ajaxSetup( { 'async': true } );
2016-11-09 19:04:47 +00:00
2017-03-27 20:32:06 +00:00
if(!geoData){
geoData = {
ip: '',
city: '',
country_name: ''
2017-07-20 23:09:21 +00:00
};
2017-03-27 20:32:06 +00:00
}
2016-06-20 22:35:41 +00:00
// Create a new message object
var visitorData = {
referrer: document.referrer,
isSubmitted: form.submitted,
formId: form._id,
lastActiveField: form.form_fields[lastActiveIndex]._id,
2016-11-09 19:04:47 +00:00
timeElapsed: timeElapsed,
language: lang,
deviceType: deviceType,
2017-03-06 21:45:11 +00:00
ipAddr: geoData.ip,
geoLocation: {
city: geoData.city,
country: geoData.country_name
}
2016-06-20 22:35:41 +00:00
};
Socket.emit('form-visitor-data', visitorData);
}
function init(){
// Make sure the Socket is connected
if (!Socket.socket) {
Socket.connect();
}
}
var service = {
send: send
};
init();
return service;
}
}());
'use strict';
angular.module('view-form').directive('keyToOption', function(){
return {
restrict: 'A',
scope: {
field: '='
},
link: function($scope, $element, $attrs, $select) {
$element.bind('keydown keypress', function(event) {
var keyCode = event.which || event.keyCode;
var index = parseInt(String.fromCharCode(keyCode))-1;
//console.log($scope.field);
if (index < $scope.field.fieldOptions.length) {
event.preventDefault();
$scope.$apply(function () {
$scope.field.fieldValue = $scope.field.fieldOptions[index].option_value;
});
}
});
}
};
});
'use strict';
angular.module('view-form').directive('keyToTruthy', ['$rootScope', function($rootScope){
return {
restrict: 'A',
scope: {
field: '='
},
link: function($scope, $element, $attrs) {
$element.bind('keydown keypress', function(event) {
var keyCode = event.which || event.keyCode;
var truthyKeyCode = $attrs.keyCharTruthy.charCodeAt(0) - 32;
var falseyKeyCode = $attrs.keyCharFalsey.charCodeAt(0) - 32;
if(keyCode === truthyKeyCode ) {
event.preventDefault();
$scope.$apply(function() {
$scope.field.fieldValue = 'true';
});
}else if(keyCode === falseyKeyCode){
event.preventDefault();
$scope.$apply(function() {
$scope.field.fieldValue = 'false';
});
}
});
}
};
}]);
'use strict';
// Configuring the Forms drop-down menus
angular.module('view-form')
.filter('formValidity', function(){
return function(formObj){
if(formObj && formObj.form_fields && formObj.visible_form_fields){
//get keys
var formKeys = Object.keys(formObj);
//we only care about things that don't start with $
var fieldKeys = formKeys.filter(function(key){
return key[0] !== '$';
});
var fields = formObj.form_fields;
var valid_count = fields.filter(function(field){
if(typeof field === 'object' && field.fieldType !== 'statement' && field.fieldType !== 'rating'){
return !!(field.fieldValue);
}
}).length;
return valid_count - (formObj.form_fields.length - formObj.visible_form_fields.length);
}
return 0;
};
});
angular.module('view-form').value('supportedFields', [
'textfield',
'textarea',
'date',
'dropdown',
'hidden',
'password',
'radio',
'legal',
'statement',
'rating',
'yes_no',
'number',
'natural'
]);
2017-03-06 21:45:11 +00:00
angular.module('view-form').constant('VIEW_FORM_URL', '/forms/:formId/render');
2016-06-20 22:35:41 +00:00
'use strict';
// SubmitForm controller
angular.module('view-form').controller('SubmitFormController', [
'$scope', '$rootScope', '$state', '$translate', 'myForm',
function($scope, $rootScope, $state, $translate, myForm) {
$scope.myform = myForm;
$translate.use(myForm.language);
2016-06-20 22:35:41 +00:00
}
]);
'use strict';
angular.module('view-form').directive('fieldIconDirective', function() {
return {
template: '<i class="{{typeIcon}}"></i>',
restrict: 'E',
scope: {
typeName: '@'
},
controller: ["$scope", function($scope){
var iconTypeMap = {
'textfield': 'fa fa-pencil-square-o',
'dropdown': 'fa fa-th-list',
'date': 'fa fa-calendar',
'checkbox': 'fa fa-check-square-o',
'radio': 'fa fa-dot-circle-o',
'email': 'fa fa-envelope-o',
'textarea': 'fa fa-pencil-square',
'legal': 'fa fa-legal',
'file': 'fa fa-cloud-upload',
'rating': 'fa fa-star-half-o',
'link': 'fa fa-link',
'scale': 'fa fa-sliders',
'stripe': 'fa fa-credit-card',
'statement': 'fa fa-quote-left',
'yes_no': 'fa fa-toggle-on',
'number': 'fa fa-slack'
};
$scope.typeIcon = iconTypeMap[$scope.typeName];
}]
};
});
'use strict';
// coffeescript's for in loop
var __indexOf = [].indexOf || function(item) {
2016-10-07 15:31:15 +00:00
for (var i = 0, l = this.length; i < l; i++) {
if (i in this && this[i] === item) return i;
}
return -1;
};
2016-06-20 22:35:41 +00:00
angular.module('view-form').directive('fieldDirective', ['$http', '$compile', '$rootScope', '$templateCache', 'supportedFields',
2016-10-07 15:31:15 +00:00
function($http, $compile, $rootScope, $templateCache, supportedFields) {
var getTemplateUrl = function(fieldType) {
var type = fieldType;
var supported_fields = [
'textfield',
'textarea',
'date',
'dropdown',
'hidden',
'password',
'radio',
'legal',
'statement',
'rating',
'yes_no',
'number',
'natural'
];
2017-03-28 00:08:45 +00:00
var templateUrl = 'form_modules/forms/base/views/directiveViews/field/';
2016-10-07 15:31:15 +00:00
if (__indexOf.call(supportedFields, type) >= 0) {
templateUrl = templateUrl+type+'.html';
}
return $templateCache.get(templateUrl);
};
2016-06-20 22:35:41 +00:00
2016-10-07 15:31:15 +00:00
return {
template: '<div>{{field.title}}</div>',
restrict: 'E',
scope: {
field: '=',
required: '&',
design: '=',
index: '=',
forms: '='
},
link: function(scope, element) {
2017-03-28 00:08:45 +00:00
2016-10-07 15:31:15 +00:00
$rootScope.chooseDefaultOption = scope.chooseDefaultOption = function(type) {
if(type === 'yes_no'){
scope.field.fieldValue = 'true';
}else if(type === 'rating'){
scope.field.fieldValue = 0;
}else if(scope.field.fieldType === 'radio'){
console.log(scope.field);
scope.field.fieldValue = scope.field.fieldOptions[0].option_value;
console.log(scope.field.fieldValue);
}else if(type === 'legal'){
scope.field.fieldValue = 'true';
$rootScope.nextField();
}
};
2016-06-20 22:35:41 +00:00
2016-10-07 15:31:15 +00:00
scope.setActiveField = $rootScope.setActiveField;
2016-06-20 22:35:41 +00:00
2016-10-07 15:31:15 +00:00
//Set format only if field is a date
if(scope.field.fieldType === 'date'){
scope.dateOptions = {
changeYear: true,
changeMonth: true,
altFormat: 'mm/dd/yyyy',
yearRange: '1900:-0',
defaultDate: 0
};
2016-06-20 22:35:41 +00:00
}
2016-10-07 15:31:15 +00:00
var fieldType = scope.field.fieldType;
if(scope.field.fieldType === 'number' || scope.field.fieldType === 'textfield' || scope.field.fieldType === 'email' || scope.field.fieldType === 'link'){
switch(scope.field.fieldType){
case 'textfield':
scope.input_type = 'text';
break;
case 'email':
scope.input_type = 'email';
scope.placeholder = 'joesmith@example.com';
break;
case 'number':
scope.input_type = 'text';
scope.validateRegex = /^-?\d+$/;
break;
default:
scope.input_type = 'url';
scope.placeholder = 'http://example.com';
break;
}
fieldType = 'textfield';
2016-06-20 22:35:41 +00:00
}
2016-10-07 15:31:15 +00:00
var template = getTemplateUrl(fieldType);
element.html(template).show();
var output = $compile(element.contents())(scope);
2016-06-20 22:35:41 +00:00
}
2016-10-07 15:31:15 +00:00
};
}]);
2016-06-20 22:35:41 +00:00
'use strict';
//TODO: DAVID: Need to refactor this
angular.module('view-form').directive('onEnterKey', ['$rootScope', function($rootScope){
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
$element.bind('keydown keypress', function(event) {
var keyCode = event.which || event.keyCode;
var onEnterKeyDisabled = false;
if($attrs.onEnterKeyDisabled !== null) onEnterKeyDisabled = $attrs.onEnterKeyDisabled;
if(keyCode === 13 && !event.shiftKey && !onEnterKeyDisabled) {
event.preventDefault();
$rootScope.$apply(function() {
$rootScope.$eval($attrs.onEnterKey);
});
}
});
}
};
}]).directive('onTabKey', ['$rootScope', function($rootScope){
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
$element.bind('keydown keypress', function(event) {
var keyCode = event.which || event.keyCode;
if(keyCode === 9 && !event.shiftKey) {
event.preventDefault();
$rootScope.$apply(function() {
$rootScope.$eval($attrs.onTabKey);
});
}
});
}
};
}]).directive('onEnterOrTabKey', ['$rootScope', function($rootScope){
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
$element.bind('keydown keypress', function(event) {
var keyCode = event.which || event.keyCode;
if((keyCode === 13 || keyCode === 9) && !event.shiftKey) {
event.preventDefault();
$rootScope.$apply(function() {
$rootScope.$eval($attrs.onEnterOrTabKey);
});
}
});
}
};
}]).directive('onTabAndShiftKey', ['$rootScope', function($rootScope){
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
$element.bind('keydown keypress', function(event) {
var keyCode = event.which || event.keyCode;
if(keyCode === 9 && event.shiftKey) {
event.preventDefault();
$rootScope.$apply(function() {
$rootScope.$eval($attrs.onTabAndShiftKey);
});
}
});
}
};
}]);
'use strict';
angular.module('view-form').directive('onFinishRender', ["$rootScope", "$timeout", function ($rootScope, $timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
//Don't do anything if we don't have a ng-repeat on the current element
if(!element.attr('ng-repeat') && !element.attr('data-ng-repeat')){
return;
}
var broadcastMessage = attrs.onFinishRender || 'ngRepeat';
if(scope.$first && !scope.$last) {
scope.$evalAsync(function () {
$rootScope.$broadcast(broadcastMessage+' Started');
});
}else if(scope.$last) {
scope.$evalAsync(function () {
// console.log(broadcastMessage+'Finished');
$rootScope.$broadcast(broadcastMessage+' Finished');
});
}
}
};
}]);
'use strict';
2016-10-07 15:31:15 +00:00
//FIXME: Should find an appropriate place for this
//Setting up jsep
2017-07-20 23:09:21 +00:00
jsep.addBinaryOp('contains', 10);
jsep.addBinaryOp('!contains', 10);
jsep.addBinaryOp('begins', 10);
jsep.addBinaryOp('!begins', 10);
jsep.addBinaryOp('ends', 10);
jsep.addBinaryOp('!ends', 10);
2016-06-20 22:35:41 +00:00
angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCounter', '$filter', '$rootScope', 'SendVisitorData',
function ($http, TimeCounter, $filter, $rootScope, SendVisitorData) {
return {
2017-03-28 00:08:45 +00:00
templateUrl: 'form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html',
2016-06-20 22:35:41 +00:00
restrict: 'E',
scope: {
myform:'='
},
controller: ["$document", "$window", "$scope", function($document, $window, $scope){
$scope.noscroll = false;
$scope.forms = {};
2017-03-06 21:45:11 +00:00
TimeCounter.restartClock();
2016-06-20 22:35:41 +00:00
var form_fields_count = $scope.myform.visible_form_fields.filter(function(field){
if(field.fieldType === 'statement' || field.fieldType === 'rating'){
return false;
}
return true;
}).length;
var nb_valid = $filter('formValidity')($scope.myform);
$scope.translateAdvancementData = {
done: nb_valid,
total: form_fields_count,
answers_not_completed: form_fields_count - nb_valid
};
$scope.reloadForm = function(){
//Reset Form
$scope.myform.submitted = false;
$scope.myform.form_fields = _.chain($scope.myform.visible_form_fields).map(function(field){
field.fieldValue = '';
return field;
}).value();
$scope.loading = false;
$scope.error = '';
$scope.selected = {
_id: '',
index: 0
};
$scope.setActiveField($scope.myform.visible_form_fields[0]._id, 0, false);
//Reset Timer
TimeCounter.restartClock();
};
//Fire event when window is scrolled
$window.onscroll = function(){
$scope.scrollPos = document.body.scrollTop || document.documentElement.scrollTop || 0;
var elemBox = document.getElementsByClassName('activeField')[0].getBoundingClientRect();
$scope.fieldTop = elemBox.top;
$scope.fieldBottom = elemBox.bottom;
//console.log($scope.forms.myForm);
var field_id;
var field_index;
if(!$scope.noscroll){
//Focus on submit button
if( $scope.selected.index === $scope.myform.visible_form_fields.length-1 && $scope.fieldBottom < 200){
field_index = $scope.selected.index+1;
field_id = 'submit_field';
$scope.setActiveField(field_id, field_index, false);
}
//Focus on field above submit button
else if($scope.selected.index === $scope.myform.visible_form_fields.length){
if($scope.fieldTop > 200){
field_index = $scope.selected.index-1;
field_id = $scope.myform.visible_form_fields[field_index]._id;
$scope.setActiveField(field_id, field_index, false);
}
}else if( $scope.fieldBottom < 0){
field_index = $scope.selected.index+1;
field_id = $scope.myform.visible_form_fields[field_index]._id;
$scope.setActiveField(field_id, field_index, false);
}else if ( $scope.selected.index !== 0 && $scope.fieldTop > 0) {
field_index = $scope.selected.index-1;
field_id = $scope.myform.visible_form_fields[field_index]._id;
$scope.setActiveField(field_id, field_index, false);
}
//console.log('$scope.selected.index: '+$scope.selected.index);
//console.log('scroll pos: '+$scope.scrollPos+' fieldTop: '+$scope.fieldTop+' fieldBottom: '+$scope.fieldBottom);
$scope.$apply();
}
};
/*
** Field Controls
*/
2016-08-23 21:45:59 +00:00
var evaluateLogicJump = function(field){
var logicJump = field.logicJump;
2016-10-07 15:31:15 +00:00
if (logicJump.expressionString && logicJump.valueB && field.fieldValue) {
var parse_tree = jsep(logicJump.expressionString);
var left, right;
if(parse_tree.left.name === 'field'){
left = field.fieldValue;
2017-07-20 23:09:21 +00:00
right = logicJump.valueB;
2016-10-07 15:31:15 +00:00
} else {
left = logicJump.valueB;
right = field.fieldValue;
}
if(field.fieldType === 'number' || field.fieldType === 'scale' || field.fieldType === 'rating'){
switch(parse_tree.operator) {
case '==':
return (parseInt(left) === parseInt(right));
case '!==':
return (parseInt(left) !== parseInt(right));
case '>':
return (parseInt(left) > parseInt(right));
case '>=':
return (parseInt(left) > parseInt(right));
case '<':
return (parseInt(left) < parseInt(right));
case '<=':
return (parseInt(left) <= parseInt(right));
default:
return false;
}
} else {
switch(parse_tree.operator) {
case '==':
return (left === right);
case '!==':
return (left !== right);
case 'contains':
return (left.indexOf(right) > -1);
case '!contains':
2017-07-20 23:09:21 +00:00
/* jshint -W018 */
2016-10-07 15:31:15 +00:00
return !(left.indexOf(right) > -1);
case 'begins':
return left.startsWith(right);
case '!begins':
return !left.startsWith(right);
case 'ends':
return left.endsWith(right);
case '!ends':
return left.endsWith(right);
default:
return false;
}
}
2016-08-23 21:45:59 +00:00
}
};
2016-06-20 22:35:41 +00:00
var getActiveField = function(){
if($scope.selected === null){
console.error('current active field is null');
throw new Error('current active field is null');
}
if($scope.selected._id === 'submit_field') {
return $scope.myform.form_fields.length - 1;
}
2017-07-20 23:09:21 +00:00
return $scope.selected.index;
2016-06-20 22:35:41 +00:00
};
$scope.setActiveField = $rootScope.setActiveField = function(field_id, field_index, animateScroll) {
if($scope.selected === null || $scope.selected._id === field_id){
//console.log('not scrolling');
//console.log($scope.selected);
return;
}
//console.log('field_id: '+field_id);
//console.log('field_index: '+field_index);
//console.log($scope.selected);
$scope.selected._id = field_id;
$scope.selected.index = field_index;
2016-08-23 21:45:59 +00:00
if(!field_index){
2016-10-07 15:31:15 +00:00
for(var i=0; i<$scope.myform.visible_form_fields.length; i++){
var currField = $scope.myform.visible_form_fields[i];
2017-07-20 23:09:21 +00:00
if(field_id === currField._id){
2016-08-23 21:45:59 +00:00
$scope.selected.index = i;
break;
}
}
}
2016-06-20 22:35:41 +00:00
var nb_valid = $filter('formValidity')($scope.myform);
$scope.translateAdvancementData = {
done: nb_valid,
total: form_fields_count,
answers_not_completed: form_fields_count - nb_valid
};
if(animateScroll){
$scope.noscroll=true;
setTimeout(function() {
$document.scrollToElement(angular.element('.activeField'), -10, 200).then(function() {
$scope.noscroll = false;
setTimeout(function() {
if (document.querySelectorAll('.activeField .focusOn').length) {
//Handle default case
document.querySelectorAll('.activeField .focusOn')[0].focus();
} else if(document.querySelectorAll('.activeField input').length) {
//Handle case for rating input
document.querySelectorAll('.activeField input')[0].focus();
} else {
//Handle case for dropdown input
document.querySelectorAll('.activeField .selectize-input')[0].focus();
}
});
});
});
}else {
setTimeout(function() {
if (document.querySelectorAll('.activeField .focusOn')[0]) {
//FIXME: DAVID: Figure out how to set focus without scroll movement in HTML Dom
document.querySelectorAll('.activeField .focusOn')[0].focus();
} else {
document.querySelectorAll('.activeField input')[0].focus();
}
});
}
SendVisitorData.send($scope.myform, getActiveField(), TimeCounter.getTimeElapsed());
};
$rootScope.nextField = $scope.nextField = function(){
2016-08-23 21:45:59 +00:00
var currField = $scope.myform.visible_form_fields[$scope.selected.index];
2016-10-07 15:31:15 +00:00
2016-08-23 21:45:59 +00:00
if($scope.selected && $scope.selected.index > -1){
//Jump to logicJump's destination if it is true
if(currField.logicJump && evaluateLogicJump(currField)){
$rootScope.setActiveField(currField.logicJump.jumpTo, null, true);
} else {
var selected_index, selected_id;
if($scope.selected.index < $scope.myform.visible_form_fields.length-1){
selected_index = $scope.selected.index+1;
selected_id = $scope.myform.visible_form_fields[selected_index]._id;
$rootScope.setActiveField(selected_id, selected_index, true);
} else if($scope.selected.index === $scope.myform.visible_form_fields.length-1) {
selected_index = $scope.selected.index+1;
selected_id = 'submit_field';
$rootScope.setActiveField(selected_id, selected_index, true);
}
}
2016-06-20 22:35:41 +00:00
}
2016-08-23 21:45:59 +00:00
2016-06-20 22:35:41 +00:00
};
$rootScope.prevField = $scope.prevField = function(){
if($scope.selected.index > 0){
var selected_index = $scope.selected.index - 1;
var selected_id = $scope.myform.visible_form_fields[selected_index]._id;
$scope.setActiveField(selected_id, selected_index, true);
}
};
/*
** Form Display Functions
*/
$scope.exitStartPage = function(){
$scope.myform.startPage.showStart = false;
if($scope.myform.visible_form_fields.length > 0){
$scope.selected._id = $scope.myform.visible_form_fields[0]._id;
}
};
$rootScope.goToInvalid = $scope.goToInvalid = function() {
document.querySelectorAll('.ng-invalid.focusOn')[0].focus();
};
2017-03-06 21:45:11 +00:00
var getDeviceData = function(){
var md = new MobileDetect(window.navigator.userAgent);
var deviceType = 'other';
if (md.tablet()){
deviceType = 'tablet';
} else if (md.mobile()) {
deviceType = 'mobile';
} else if (!md.is('bot')) {
deviceType = 'desktop';
}
return {
type: deviceType,
name: window.navigator.platform
2017-07-20 23:09:21 +00:00
};
2017-03-06 21:45:11 +00:00
};
var getIpAndGeo = function(){
//Get Ip Address and GeoLocation Data
2017-07-20 23:09:21 +00:00
$.ajaxSetup( { 'async': false } );
2017-03-10 19:26:07 +00:00
var geoData = $.getJSON('https://freegeoip.net/json/').responseJSON;
2017-07-20 23:09:21 +00:00
$.ajaxSetup( { 'async': true } );
2017-03-06 21:45:11 +00:00
return {
ipAddr: geoData.ip,
geoLocation: {
City: geoData.city,
Country: geoData.country_name
}
2017-07-20 23:09:21 +00:00
};
2017-03-06 21:45:11 +00:00
};
2016-06-20 22:35:41 +00:00
$rootScope.submitForm = $scope.submitForm = function() {
var _timeElapsed = TimeCounter.stopClock();
$scope.loading = true;
var form = _.cloneDeep($scope.myform);
2017-03-06 21:45:11 +00:00
var deviceData = getDeviceData();
form.device = deviceData;
var geoData = getIpAndGeo();
form.ipAddr = geoData.ipAddr;
form.geoLocation = geoData.geoLocation;
console.log(geoData);
2016-06-20 22:35:41 +00:00
2017-03-06 21:45:11 +00:00
form.timeElapsed = _timeElapsed;
2016-06-20 22:35:41 +00:00
form.percentageComplete = $filter('formValidity')($scope.myform) / $scope.myform.visible_form_fields.length * 100;
delete form.visible_form_fields;
for(var i=0; i < $scope.myform.form_fields.length; i++){
if($scope.myform.form_fields[i].fieldType === 'dropdown' && !$scope.myform.form_fields[i].deletePreserved){
$scope.myform.form_fields[i].fieldValue = $scope.myform.form_fields[i].fieldValue.option_value;
}
}
setTimeout(function () {
$scope.submitPromise = $http.post('/forms/' + $scope.myform._id, form)
2017-07-20 23:09:21 +00:00
.success(function (data, status) {
2016-06-20 22:35:41 +00:00
$scope.myform.submitted = true;
$scope.loading = false;
SendVisitorData.send($scope.myform, getActiveField(), _timeElapsed);
})
.error(function (error) {
$scope.loading = false;
console.error(error);
$scope.error = error.message;
});
}, 500);
};
//Reload our form
2017-07-20 23:09:21 +00:00
$scope.reloadForm();
2016-06-20 22:35:41 +00:00
}]
};
}
]);
'use strict';
//Forms service used for communicating with the forms REST endpoints
angular.module('view-form').service('CurrentForm',
function(){
//Private variables
var _form = {};
//Public Methods
this.getForm = function() {
return _form;
};
this.setForm = function(form) {
_form = form;
};
}
);
'use strict';
//Forms service used for communicating with the forms REST endpoints
2017-03-06 21:45:11 +00:00
angular.module('view-form').factory('Forms', ['$resource', 'VIEW_FORM_URL',
function($resource, VIEW_FORM_URL) {
return $resource(VIEW_FORM_URL, {
2016-06-20 22:35:41 +00:00
formId: '@_id'
}, {
'get' : {
method: 'GET',
transformResponse: function(data, header) {
var form = angular.fromJson(data);
form.visible_form_fields = _.filter(form.form_fields, function(field){
return (field.deletePreserved === false);
});
return form;
}
},
'update': {
method: 'PUT'
},
'save': {
method: 'POST'
}
});
}
]);
(function () {
'use strict';
// Create the Socket.io wrapper service
function Socket($timeout, $window) {
2017-07-20 23:09:21 +00:00
var service;
2016-06-20 22:35:41 +00:00
// Connect to Socket.io server
function connect(url) {
service.socket = io(url, {'transports': ['websocket', 'polling']});
}
// Wrap the Socket.io 'emit' method
function emit(eventName, data) {
if (service.socket) {
service.socket.emit(eventName, data);
}
}
// Wrap the Socket.io 'on' method
function on(eventName, callback) {
if (service.socket) {
service.socket.on(eventName, function (data) {
$timeout(function () {
callback(data);
});
});
}
}
// Wrap the Socket.io 'removeListener' method
function removeListener(eventName) {
if (service.socket) {
service.socket.removeListener(eventName);
}
}
2017-07-20 23:09:21 +00:00
service = {
connect: connect,
emit: emit,
on: on,
removeListener: removeListener,
socket: null
};
var url = '';
if($window.socketUrl && $window.socketPort){
url = window.location.protocol + '//' + $window.socketUrl + ':' + $window.socketPort;
} else if ($window.socketUrl && !$window.socketPort){
url = window.location.protocol + '//' + $window.socketUrl;
} else if ($window.socketPort){
url = window.location.protocol + '//' + window.location.hostname + ':' + $window.socketPort;
} else {
url = window.location.protocol + '//' + window.location.hostname;
}
connect(url);
return service;
2016-06-20 22:35:41 +00:00
}
2017-07-20 23:09:21 +00:00
angular
.module('view-form')
.factory('Socket', Socket);
Socket.$inject = ['$timeout', '$window'];
2016-06-20 22:35:41 +00:00
}());
'use strict';
angular.module('view-form').service('TimeCounter', [
function(){
2017-07-20 23:09:21 +00:00
var _startTime, _endTime = null;
2016-06-20 22:35:41 +00:00
this.timeSpent = 0;
this.restartClock = function(){
_startTime = Date.now();
_endTime = null;
// console.log('Clock Started');
};
this.getTimeElapsed = function(){
if(_startTime) {
return Math.abs(Date.now().valueOf() - _startTime.valueOf()) / 1000;
}
};
this.stopClock = function(){
if(_startTime && _endTime === null){
_endTime = Date.now();
this.timeSpent = Math.abs(_endTime.valueOf() - _startTime.valueOf())/1000;
this._startTime = this._endTime = null;
return this.timeSpent;
}
2017-07-20 23:09:21 +00:00
return new Error('Clock has not been started');
2016-06-20 22:35:41 +00:00
};
this.clockStarted = function(){
return !!this._startTime;
};
}
]);