'use strict'; // Init the application configuration module for AngularJS application var ApplicationConfiguration = (function() { // Init module configuration options var applicationModuleName = 'TellForm-Form'; var applicationModuleVendorDependencies = ['duScroll', 'ui.select', 'ngSanitize', 'vButton', 'ngResource', 'TellForm-Form.form_templates', 'ui.router', 'ui.bootstrap', 'pascalprecht.translate']; // 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', { viewAdminSettings: 'viewAdminSettings', editAdminSettings: 'editAdminSettings', editForm: 'editForm', viewPrivateForm: 'viewPrivateForm' }); //User Role constants angular.module(ApplicationConfiguration.applicationModuleName).constant('USER_ROLES', { admin: 'admin', normal: 'user', superuser: 'superuser' }); //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]); }); angular.module('TellForm-Form.form_templates', []).run(['$templateCache', function($templateCache) { "use strict"; $templateCache.put("form_modules/forms/base/views/form-unauthorized.client.view.html", "

Not Authorized to Access Form

The form you are trying to access is currently private and not accesible publically.
If you are the owner of the form, you can set it to \"Public\" in the \"Configuration\" panel in the form admin.
"); $templateCache.put("form_modules/forms/base/views/submit-form.client.view.html", "
"); $templateCache.put("form_modules/forms/base/views/directiveViews/entryPage/startPage.html", "

{{pageData.introTitle}}

{{pageData.introParagraph}}

"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/date.html", "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}

"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/dropdown.html", "
0\">

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}


"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/hidden.html", ""); $templateCache.put("form_modules/forms/base/views/directiveViews/field/legal.html", "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}


{{field.description}}


"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/radio.html", "
0\">

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}


"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/rating.html", "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}

"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/statement.html", "

{{field.title}}

{{field.description}}

{{field.description}}


"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/textarea.html", "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{ 'NEWLINE' | translate }}

{{field.description}}

Press SHIFT+ENTER to add a newline
{{ 'ENTER' | translate }}
"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/textfield.html", "

{{index+1}} {{field.title}} ({{ 'OPTIONAL' | translate }})

{{field.description}}

{{ 'ENTER' | translate }}
"); $templateCache.put("form_modules/forms/base/views/directiveViews/field/yes_no.html", "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}


"); $templateCache.put("form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html", "

{{myform.startPage.introTitle}}

{{myform.startPage.introParagraph}}

{{ 'COMPLETING_NEEDED' | translate:translateAdvancementData }}
{{ 'ENTER' | translate }}

{{ 'ADVANCEMENT' | translate:translateAdvancementData }}

{{ 'FORM_SUCCESS' | translate }}

{{myform.endPage.title}}

{{myform.endPage.paragraph}}

"); }]); 'use strict'; // Use Application configuration module to register a new module ApplicationConfiguration.registerModule('view-form', [ 'ngFileUpload', 'ui.date', 'angular-input-stars' ]); '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 don’t 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: 'J’accepte', LEGAL_NO_ACCEPT: 'Je n’accepte 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 don’t 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 don’t 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', LEGAL_ACCEPT: 'Yo acepto', LEGAL_NO_ACCEPT: 'Yo no acepto', 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; }] }, controller: 'SubmitFormController', controllerAs: 'ctrl' }). state('unauthorizedFormAccess', { url: '/forms/unauthorized', templateUrl: '/static/form_modules/forms/base/views/form-unauthorized.client.view.html', }); } ]); (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) { 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'; } else if (!md.is('bot')) { deviceType = 'desktop'; } $.ajaxSetup( { 'async': false } ); var geoData = $.getJSON('https://freegeoip.net/json/').responseJSON; $.ajaxSetup( { 'async': true } ); if(!geoData){ geoData = { ip: '', city: '', country_name: '' }; } // Create a new message object var visitorData = { referrer: document.referrer, isSubmitted: form.submitted, formId: form._id, lastActiveField: form.form_fields[lastActiveIndex]._id, timeElapsed: timeElapsed, language: lang, deviceType: deviceType, ipAddr: geoData.ip, geoLocation: { city: geoData.city, country: geoData.country_name } }; 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: '=', nextField: '&' }, 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; console.log($scope); if(keyCode === truthyKeyCode ) { event.preventDefault(); $scope.$apply(function() { $scope.field.fieldValue = 'true'; if($attrs.onValidKey){ $scope.$root.$eval($attrs.onValidKey); } }); }else if(keyCode === falseyKeyCode){ event.preventDefault(); $scope.$apply(function() { $scope.field.fieldValue = 'false'; if($attrs.onValidKey){ $scope.$root.$eval($attrs.onValidKey); } }); } }); } }; }]); '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 !== 'rating' && field.fieldType !== 'statement'){ return !!(field.fieldValue); } else if (field.fieldType === 'rating'){ return true; } }).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' ]); angular.module('view-form').constant('VIEW_FORM_URL', '/forms/:formId/render'); '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); } ]); 'use strict'; angular.module('view-form').directive('fieldIconDirective', function() { return { template: '', 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) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; angular.module('view-form').directive('fieldDirective', ['$http', '$compile', '$rootScope', '$templateCache', 'supportedFields', 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' ]; var templateUrl = 'form_modules/forms/base/views/directiveViews/field/'; if (__indexOf.call(supportedFields, type) >= 0) { templateUrl = templateUrl+type+'.html'; } return $templateCache.get(templateUrl); }; return { template: '
{{field.title}}
', restrict: 'E', scope: { field: '=', required: '&', design: '=', index: '=', forms: '=' }, link: function(scope, element) { $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(); } }; scope.nextField = $rootScope.nextField; scope.setActiveField = $rootScope.setActiveField; //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 }; } 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'; } var template = getTemplateUrl(fieldType); element.html(template).show(); var output = $compile(element.contents())(scope); } }; }]); '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'; //FIXME: Should find an appropriate place for this //Setting up jsep jsep.addBinaryOp('contains', 10); jsep.addBinaryOp('!contains', 10); jsep.addBinaryOp('begins', 10); jsep.addBinaryOp('!begins', 10); jsep.addBinaryOp('ends', 10); jsep.addBinaryOp('!ends', 10); angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCounter', '$filter', '$rootScope', 'SendVisitorData', function ($http, TimeCounter, $filter, $rootScope, SendVisitorData) { return { templateUrl: 'form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html', restrict: 'E', scope: { myform:'=' }, controller: ["$document", "$window", "$scope", function($document, $window, $scope){ $scope.noscroll = false; $scope.forms = {}; TimeCounter.restartClock(); var form_fields_count = $scope.myform.visible_form_fields.filter(function(field){ if(field.fieldType === 'statement'){ 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 */ var evaluateLogicJump = function(field){ var logicJump = field.logicJump; 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; right = logicJump.valueB; } 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': /* jshint -W018 */ 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; } } } }; 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; } return $scope.selected.index; }; $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; if(!field_index){ for(var i=0; i<$scope.myform.visible_form_fields.length; i++){ var currField = $scope.myform.visible_form_fields[i]; if(field_id === currField._id){ $scope.selected.index = i; break; } } } 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(){ var currField = $scope.myform.visible_form_fields[$scope.selected.index]; 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); } } } }; $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(); }; 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 }; }; var getIpAndGeo = function(){ //Get Ip Address and GeoLocation Data $.ajaxSetup( { 'async': false } ); var geoData = $.getJSON('https://freegeoip.net/json/').responseJSON; $.ajaxSetup( { 'async': true } ); if(!geoData || !geoData.ip){ geoData = { ip: 'Adblocker' }; } return { ipAddr: geoData.ip, geoLocation: { City: geoData.city, Country: geoData.country_name } }; }; $rootScope.submitForm = $scope.submitForm = function() { var _timeElapsed = TimeCounter.stopClock(); $scope.loading = true; var form = _.cloneDeep($scope.myform); var deviceData = getDeviceData(); form.device = deviceData; var geoData = getIpAndGeo(); form.ipAddr = geoData.ipAddr; form.geoLocation = geoData.geoLocation; console.log(geoData); form.timeElapsed = _timeElapsed; 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) .success(function (data, status) { $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 $scope.reloadForm(); }] }; } ]); '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 angular.module('view-form').factory('Forms', ['$resource', 'VIEW_FORM_URL', function($resource, VIEW_FORM_URL) { return $resource(VIEW_FORM_URL, { 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) { var service; // 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); } } service = { connect: connect, emit: emit, on: on, removeListener: removeListener, socket: null }; console.log($window.socketUrl); var url = ''; if($window.socketUrl && $window.socketPort){ url = window.location.protocol + '//' + $window.socketUrl + ':' + $window.socketPort; } else if ($window.socketUrl){ 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; } angular .module('view-form') .factory('Socket', Socket); Socket.$inject = ['$timeout', '$window']; }()); 'use strict'; angular.module('view-form').service('TimeCounter', [ function(){ var _startTime, _endTime = null; 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; } return new Error('Clock has not been started'); }; this.clockStarted = function(){ return !!this._startTime; }; } ]);