'use strict'; // Init the application configuration module for AngularJS application var ApplicationConfiguration = (function() { // Init module configuration options var applicationModuleName = 'NodeForm'; var applicationModuleVendorDependencies = ['duScroll', 'ui.select', 'cgBusy', 'ngSanitize', 'vButton', 'ngResource', 'NodeForm.templates', 'ui.router', 'ui.bootstrap', 'ui.utils', 'pascalprecht.translate', 'ng.deviceDetector']; // 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('NodeForm.templates', []).run(['$templateCache', function($templateCache) { "use strict"; $templateCache.put("modules/core/views/header.client.view.html", "
"); $templateCache.put("modules/forms/admin/views/admin-form.client.view.html", "
Your TellForm is permanently at this URL
Copy and Paste this to add your TellForm to your website
{{ 'BACKGROUND_COLOR' | translate }}
{{ 'QUESTION_TEXT_COLOR' | translate }}
{{ 'ANSWER_TEXT_COLOR' | translate }}
{{ 'BTN_BACKGROUND_COLOR' | translate }}
{{ 'BTN_TEXT_COLOR' | translate }}
"); $templateCache.put("modules/forms/admin/views/list-forms.client.view.html", "

My Forms

{{ 'CREATE_A_NEW_FORM' | translate }}
Name
Language

{{ form.submissions.length }} responses

Form Paused
"); $templateCache.put("modules/forms/base/views/submit-form.client.view.html", "
"); $templateCache.put("modules/forms/admin/views/adminTabs/analyze.html", ""); $templateCache.put("modules/forms/admin/views/adminTabs/configure.html", ""); $templateCache.put("modules/forms/admin/views/adminTabs/create.html", ""); $templateCache.put("modules/forms/admin/views/adminTabs/design.html", "
{{ 'BACKGROUND_COLOR' | translate }}
{{ 'QUESTION_TEXT_COLOR' | translate }}
{{ 'ANSWER_TEXT_COLOR' | translate }}
{{ 'BTN_BACKGROUND_COLOR' | translate }}
{{ 'BTN_TEXT_COLOR' | translate }}
"); $templateCache.put("modules/forms/admin/views/directiveViews/cgBusy/update-form-message-TypeA.html", "
{{$message}}
"); $templateCache.put("modules/forms/admin/views/directiveViews/cgBusy/update-form-message-TypeB.html", "
{{$message}}
"); $templateCache.put("modules/forms/admin/views/directiveViews/form/configure-form.client.view.html", "
{{ 'FORM_NAME' | translate }}
{{ 'FORM_STATUS' | translate }}
Language
* required
{{ 'GA_TRACKING_CODE' | translate }}
{{ 'DISPLAY_FOOTER' | translate }}
Display Start Page?
"); $templateCache.put("modules/forms/admin/views/directiveViews/form/edit-form.client.view.html", "

{{ 'ADD_FIELD_LG' | translate }}

{{ 'ADD_FIELD_MD' | translate }}

{{ 'ADD_FIELD_SM' | translate }}

{{ 'WELCOME_SCREEN' | translate }}

{{ 'PREVIEW_START_PAGE' | translate }}

    {{myform.startPage.introTitle}}

    {{myform.startPage.introParagraph}}

{{ 'EDIT_START_PAGE' | translate }}


{{ 'INTRO_TITLE' | translate }}:
{{ 'INTRO_PARAGRAPH' | translate }}:
{{ 'INTRO_BTN' | translate }}:


Buttons:
{{ 'BUTTON_TEXT' | translate }}
{{ 'BUTTON_LINK' | translate }}


{{field.title}} *

{{ 'CLICK_FIELDS_FOOTER' | translate }}


"); $templateCache.put("modules/forms/admin/views/directiveViews/form/edit-submissions-form.client.view.html", "
{{ 'TOTAL_VIEWS' | translate }}
{{ 'RESPONSES' | translate }}
{{ 'COMPLETION_RATE' | translate }}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{myform.analytics.views}}
{{myform.analytics.submissions}}
{{myform.analytics.conversionRate | number:0}}%
{{ AverageTimeElapsed | secondsToDateTime | date:'mm:ss'}}
{{ 'DESKTOP_AND_LAPTOP' | translate }}
{{ 'TABLETS' | translate }}
{{ 'PHONES' | translate }}
{{ 'OTHER' | translate }}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.desktop.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.tablet.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.tablet.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.other.visits}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.desktop.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.tablet.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.phone.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.other.responses}}
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.desktop.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.tablet.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.phone.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.other.completion}}%
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.desktop.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.tablet.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.phone.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.other.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'FIELD_TITLE' | translate }}
{{ 'FIELD_VIEWS' | translate }}
{{ 'FIELD_RESPONSES' | translate }}
{{ 'FIELD_DROPOFF' | translate }}
{{fieldStats.field.title}}
{{fieldStats.totalViews}}
{{fieldStats.responses}}
{{fieldStats.continueRate}}%

#{{value.title}}{{ 'PERCENTAGE_COMPLETE' | translate }}{{ 'TIME_ELAPSED' | translate }}{{ 'DEVICE' | translate }}{{ 'LOCATION' | translate }}{{ 'IP_ADDRESS' | translate }}{{ 'DATE_SUBMITTED' | translate }} (UTC)
{{$index+1}}{{field.fieldValue}}{{row.percentageComplete}}%{{row.timeElapsed | secondsToDateTime | date:'mm:ss'}}{{row.device.name}}, {{row.device.type}}{{row.geoLocation.City}}, {{row.geoLocation.Country}}{{row.ipAddr}}{{row.created | date:'yyyy-MM-dd HH:mm:ss'}}
"); $templateCache.put("modules/forms/base/views/directiveViews/entryPage/startPage.html", "

{{pageData.introTitle}}

{{pageData.introParagraph}}

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

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

{{field.description}}

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

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

{{field.description}}


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

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

{{field.file.originalname}}
{{ UPLOAD_FILE | translate }}
"); $templateCache.put("modules/forms/base/views/directiveViews/field/hidden.html", ""); $templateCache.put("modules/forms/base/views/directiveViews/field/legal.html", "

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


{{field.description}}


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

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

{{field.description}}


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

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

{{field.description}}

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

{{field.title}}

{{field.description}}

{{field.description}}


"); $templateCache.put("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("modules/forms/base/views/directiveViews/field/textfield.html", "

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

{{field.description}}

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

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

{{field.description}}


"); $templateCache.put("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 }}

"); $templateCache.put("modules/users/views/authentication/access-denied.client.view.html", "

{{ 'ACCESS_DENIED_TEXT' | translate }}

{{ 'SIGNIN_BTN' | translate }}
"); $templateCache.put("modules/users/views/authentication/signin.client.view.html", "
Error:
"); $templateCache.put("modules/users/views/authentication/signup-success.client.view.html", "

{{ 'SUCCESS_HEADER' | translate }}

{{ 'SUCCESS_TEXT' | translate }}

{{ 'NOT_ACTIVATED_YET' | translate }}



{{ 'BEFORE_YOU_CONTINUE' | translate }} polydaic@gmail.com

"); $templateCache.put("modules/users/views/authentication/signup.client.view.html", "
{{'SIGNUP_ERROR_TEXT' | translate}}:
"); $templateCache.put("modules/users/views/password/forgot-password.client.view.html", "
{{error}}
{{success}}
"); $templateCache.put("modules/users/views/password/reset-password-invalid.client.view.html", "

{{ 'PASSWORD_RESET_INVALID' | translate }}

{{ 'ASK_FOR_NEW_PASSWORD' | translate }}
"); $templateCache.put("modules/users/views/password/reset-password-success.client.view.html", "

{{ 'PASSWORD_RESET_SUCCESS' | translate }}

{{ 'CONTINUE_TO_LOGIN' | translate }}
"); $templateCache.put("modules/users/views/password/reset-password.client.view.html", "

Reset your password

{{error}}
{{success}}
"); $templateCache.put("modules/users/views/settings/change-password.client.view.html", "

Change your password


{{ 'PASSWORD_CHANGE_SUCCESS' | translate }}
"); $templateCache.put("modules/users/views/settings/edit-profile.client.view.html", "

Edit your profile

{{ 'PROFILE_SAVE_SUCCESS' | translate }}
{{ 'PROFILE_SAVE_ERROR' | translate }}
{{ 'FIRST_NAME_LABEL' | translate }}
{{ 'LAST_NAME_LABEL' | translate }}

{{ 'LANGUAGE_LABEL' | translate }}
{{ 'USERNAME_LABEL' | translate }}
{{ 'EMAIL_LABEL' | translate }}
"); $templateCache.put("modules/users/views/settings/social-accounts.client.view.html", "

Connected social accounts:

Connect other social accounts:

"); $templateCache.put("modules/users/views/verify/resend-verify-email.client.view.html", "
{{error}}

Verification Email has been Sent

{{ 'VERIFICATION_EMAIL_SENT' | translate }} {{username}}.
{{ 'NOT_ACTIVATED_YET' | translate }}

{{ 'CHECK_YOUR_EMAIL' | translate }} polydaic@gmail.com

"); $templateCache.put("modules/users/views/verify/verify-account.client.view.html", "

{{ 'VERIFY_SUCCESS' | translate }}

"); }]); 'use strict'; // Use Application configuration module to register a new module ApplicationConfiguration.registerModule('view-form', [ 'ngFileUpload', 'ui.router.tabs', 'ui.date', 'ui.sortable', 'angular-input-stars', 'pascalprecht.translate' ]); '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: 'I accept', LEGAL_NO_ACCEPT: 'I don’t accept', 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", "$stateParams", function (Forms, $stateParams) { return Forms.get({formId: $stateParams.formId}).$promise; }] }, controller: 'SubmitFormController', controllerAs: 'ctrl' }); } ]); (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('//freegeoip.net/json/').responseJSON; $.ajaxSetup( { "async": true } ); // 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: '=' }, 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' ]); 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 = '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.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); /** * Calculate a 32 bit FNV-1a hash * Found here: https://gist.github.com/vaiorabbit/5657561 * Ref.: http://isthe.com/chongo/tech/comp/fnv/ * * @param {string} str the input value * @param {boolean} [asString=false] set to true to return the hash value as * 8-digit hex string instead of an integer * @param {integer} [seed] optionally pass the hash of the previous chunk * @returns {integer | string} */ function hashFnv32a(str, asString, seed) { /*jshint bitwise:false */ var i, l, hval = (seed === undefined) ? 0x811c9dc5 : seed; for (i = 0, l = str.length; i < l; i++) { hval ^= str.charCodeAt(i); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } if( asString ){ // Convert to 8 digit hex string return ("0000000" + (hval >>> 0).toString(16)).substr(-8); } return hval >>> 0; } angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCounter', '$filter', '$rootScope', 'SendVisitorData', function ($http, TimeCounter, $filter, $rootScope, SendVisitorData) { return { templateUrl: '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' || 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 */ var evaluateLogicJump = function(field){ console.log('evaluateLogicJump'); console.log(field.fieldValue); var logicJump = field.logicJump; if (logicJump.expressionString && logicJump.valueB && field.fieldValue) { var parse_tree = jsep(logicJump.expressionString); var left, right; console.log(parse_tree); 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': 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; } else { 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('//freegeoip.net/json/').responseJSON; $.ajaxSetup( { "async": true } ); 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, headers) { $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 angular .module('view-form') .factory('Socket', Socket); Socket.$inject = ['$timeout', '$window']; function Socket($timeout, $window) { var service = { connect: connect, emit: emit, on: on, removeListener: removeListener, socket: null }; connect(window.location.protocol+'//'+window.location.hostname+':'+$window.socketPort); return 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); } } } }()); 'use strict'; angular.module('view-form').service('TimeCounter', [ function(){ var _startTime, _endTime = null, that=this; 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; }else{ return new Error('Clock has not been started'); } }; this.clockStarted = function(){ return !!this._startTime; }; } ]);