diff --git a/app/controllers/forms.server.controller.js b/app/controllers/forms.server.controller.js index 03684052..2633d648 100644 --- a/app/controllers/forms.server.controller.js +++ b/app/controllers/forms.server.controller.js @@ -164,7 +164,7 @@ var readForRender = exports.readForRender = function(req, res) { delete newForm.__v; delete newForm.created; - if(!newForm.startPage.showStart){ + if(newForm.startPage && !newForm.startPage.showStart){ delete newForm.startPage; } @@ -175,20 +175,31 @@ var readForRender = exports.readForRender = function(req, res) { * Update a form */ exports.update = function(req, res) { + var form = req.form; var updatedForm = req.body.form; + if(form.form_fields === undefined){ + form.form_fields = []; + } - delete updatedForm.__v; - delete updatedForm.created; + if(form.analytics === undefined){ + form.analytics = { + visitors: [], + gaCode: '' + } + } if (req.body.changes) { var formChanges = req.body.changes; formChanges.forEach(function (change) { - diff.applyChange(form, true, change); + diff.applyChange(form._doc, true, change); }); } else { - //Unless we have 'admin' privileges, updating form admin is disabled + + delete updatedForm.__v; + delete updatedForm.created; + //Unless we have 'admin' privileges, updating the form's admin is disabled if(updatedForm && req.user.roles.indexOf('admin') === -1) { delete updatedForm.admin; } @@ -200,7 +211,7 @@ exports.update = function(req, res) { //Do this so we can create duplicate fields var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$'); - for(var i=0; i { + console.log(body); + var geoData = body; + visitorsData[current_socket.id].geoLocation = { + city: geoData.city, + country: geoData.country_name + } + + if (data.isSubmitted && !data.isSaved) { + visitorsData[current_socket.id].isSaved = true; + saveVisitorData(data, function() { + current_socket.disconnect(true); + }); + } + }); }); current_socket.on('disconnect', function() { diff --git a/app/views/form.server.view.html b/app/views/form.server.view.html index b07466ed..be1bd664 100644 --- a/app/views/form.server.view.html +++ b/app/views/form.server.view.html @@ -76,11 +76,6 @@ - diff --git a/package.json b/package.json index 0dda7145..0a10e435 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "prerender-node": "^2.2.1", "random-js": "^1.0.8", "raven": "^0.9.0", + "request": "^2.83.0", "socket.io": "^1.4.6", "socket.io-redis": "^1.0.0", "swig": "~1.4.1", diff --git a/public/dist/form_populate_template_cache.js b/public/dist/form_populate_template_cache.js index 2e0669d5..5aed5566 100644 --- a/public/dist/form_populate_template_cache.js +++ b/public/dist/form_populate_template_cache.js @@ -1,33 +1,35 @@ angular.module('TellForm-Form.form_templates', []).run(['$templateCache', function($templateCache) { "use strict"; + $templateCache.put("form_modules/forms/base/views/form-not-found.client.view.html", + "

404 - Form Does not Exist

The form you are trying to access does not exist. Sorry about that!
"); $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.
"); + "

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}}

"); + "

{{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}}


"); + "
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}}


"); + "
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}}


"); + "

{{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 }}
"); + "

{{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}}


"); + "

{{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}}

"); + "

{{myform.startPage.introTitle}}

{{myform.startPage.introParagraph}}

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

{{ 'ADVANCEMENT' | translate:translateAdvancementData }}

{{ 'FORM_SUCCESS' | translate }}

{{myform.endPage.title}}

{{myform.endPage.paragraph}}

"); }]); diff --git a/public/form_modules/forms/base/controllers/submit-form.client.controller.js b/public/form_modules/forms/base/controllers/submit-form.client.controller.js index c3824171..bca7f3f0 100644 --- a/public/form_modules/forms/base/controllers/submit-form.client.controller.js +++ b/public/form_modules/forms/base/controllers/submit-form.client.controller.js @@ -5,6 +5,9 @@ angular.module('view-form').controller('SubmitFormController', [ '$scope', '$rootScope', '$state', '$translate', 'myForm', function($scope, $rootScope, $state, $translate, myForm) { $scope.myform = myForm; + + $(".loader").fadeOut("slow"); + document.body.style.background = myForm.design.colors.backgroundColor; $translate.use(myForm.language); } ]); diff --git a/public/form_modules/forms/base/directives/on-enter-key.client.directive.js b/public/form_modules/forms/base/directives/on-enter-key.client.directive.js index afb9e135..eb4e5cfd 100644 --- a/public/form_modules/forms/base/directives/on-enter-key.client.directive.js +++ b/public/form_modules/forms/base/directives/on-enter-key.client.directive.js @@ -25,7 +25,7 @@ angular.module('view-form').directive('onEnterKey', ['$rootScope', function($roo return { restrict: 'A', link: function($scope, $element, $attrs) { - $element.bind('keydown keypress', function(event) { + $element.bind('keyup keypress', function(event) { var keyCode = event.which || event.keyCode; @@ -65,6 +65,8 @@ angular.module('view-form').directive('onEnterKey', ['$rootScope', function($roo var keyCode = event.which || event.keyCode; if(keyCode === 9 && event.shiftKey) { + + console.log('onTabAndShiftKey'); event.preventDefault(); $rootScope.$apply(function() { $rootScope.$eval($attrs.onTabAndShiftKey); diff --git a/public/form_modules/forms/base/directives/submit-form.client.directive.js b/public/form_modules/forms/base/directives/submit-form.client.directive.js index b1be6d38..3559209f 100644 --- a/public/form_modules/forms/base/directives/submit-form.client.directive.js +++ b/public/form_modules/forms/base/directives/submit-form.client.directive.js @@ -9,8 +9,8 @@ jsep.addBinaryOp('!begins', 10); jsep.addBinaryOp('ends', 10); jsep.addBinaryOp('!ends', 10); -angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCounter', '$filter', '$rootScope', 'SendVisitorData', '$translate', - function ($http, TimeCounter, $filter, $rootScope, SendVisitorData, $translate) { +angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCounter', '$filter', '$rootScope', 'SendVisitorData', '$translate', '$timeout', + function ($http, TimeCounter, $filter, $rootScope, SendVisitorData, $translate, $timeout) { return { templateUrl: 'form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html', restrict: 'E', @@ -19,7 +19,8 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun ispreview: '=' }, controller: function($document, $window, $scope){ - $scope.noscroll = false; + var NOSCROLL = false; + var FORM_ACTION_ID = 'submit_field'; $scope.forms = {}; //Don't start timer if we are looking at a design preview @@ -59,43 +60,6 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun 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; - - 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); - } - $scope.$apply(); - } - }; - /* ** Field Controls */ @@ -163,35 +127,46 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun throw new Error('current active field is null'); } - if($scope.selected._id === 'submit_field') { + if($scope.selected._id === FORM_ACTION_ID) { return $scope.myform.form_fields.length - 1; } return $scope.selected.index; + }; + $scope.isActiveField = function(field){ + if($scope.selected._id === field._id) { + return true + } + return false; }; $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); + if($scope.selected === null || (!field_id && field_index === null) ) { + return; + } + + if(!field_id){ + field_id = $scope.myform.visible_form_fields[field_index]._id; + } else if(field_index === null){ + field_index = $scope.myform.visible_form_fields.length - $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++){ + 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; + if(currField['_id'] == field_id){ + field_index = i; break; } } } + if($scope.selected._id === field_id){ + return; + } + + $scope.selected._id = field_id; + $scope.selected.index = field_index; + + var nb_valid = $filter('formValidity')($scope.myform); $scope.translateAdvancementData = { done: nb_valid, @@ -200,10 +175,10 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun }; if(animateScroll){ - $scope.noscroll=true; + NOSCROLL=true; setTimeout(function() { $document.scrollToElement(angular.element('.activeField'), -10, 200).then(function() { - $scope.noscroll = false; + NOSCROLL = false; setTimeout(function() { if (document.querySelectorAll('.activeField .focusOn').length) { //Handle default case @@ -218,54 +193,109 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun }); }); }); - } 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 if (document.querySelectorAll('.activeField input')[0]){ - document.querySelectorAll('.activeField input')[0].focus(); - } - }); - } - - //Only send analytics data if form has not been submitted - if(!$scope.myform.submitted){ - 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); - } + $scope.$watch('selected.index', function(oldValue, newValue){ + if(oldValue !== newValue && newValue < $scope.myform.form_fields.length){ + //Only send analytics data if form has not been submitted + if(!$scope.myform.submitted){ + console.log('SendVisitorData.send()'); + SendVisitorData.send($scope.myform, newValue, TimeCounter.getTimeElapsed()); } - } + } + }); + //Fire event when window is scrolled + $window.onscroll = function(){ + if(!NOSCROLL){ + + var scrollTop = $(window).scrollTop(); + var elemBox = document.getElementsByClassName('activeField')[0].getBoundingClientRect(); + var fieldTop = elemBox.top; + var fieldBottom = elemBox.bottom; + + var field_id, field_index; + var elemHeight = $('.activeField').height(); + + var submitSectionHeight = $('.form-actions').height(); + var maxScrollTop = $(document).height() - $(window).height(); + var fieldWrapperHeight = $('form_fields').height(); + + var selector = 'form > .field-directive:nth-of-type(' + String($scope.myform.visible_form_fields.length - 1)+ ')' + var fieldDirectiveHeight = $(selector).height() + var scrollPosition = maxScrollTop - submitSectionHeight - fieldDirectiveHeight*1.2; + + var fractionToJump = 0.9; + + //Focus on field above submit form button + if($scope.selected.index === $scope.myform.visible_form_fields.length){ + if(scrollTop < scrollPosition){ + field_index = $scope.selected.index-1; + $scope.setActiveField(null, field_index, false); + } + } + + //Focus on submit form button + else if($scope.selected.index === $scope.myform.visible_form_fields.length-1 && scrollTop > scrollPosition){ + field_index = $scope.selected.index+1; + $scope.setActiveField(FORM_ACTION_ID, field_index, false); + } + + //If we scrolled bellow the current field, move to next field + else if(fieldBottom < elemHeight * fractionToJump && $scope.selected.index < $scope.myform.visible_form_fields.length-1 ){ + field_index = $scope.selected.index+1; + $scope.setActiveField(null, field_index, false); + } + //If we scrolled above the current field, move to prev field + else if ( $scope.selected.index !== 0 && fieldTop > elemHeight * fractionToJump) { + field_index = $scope.selected.index-1; + $scope.setActiveField(null, field_index, false); + } + } + + $scope.$apply(); + }; + + $rootScope.nextField = $scope.nextField = function(){ + if($scope.selected && $scope.selected.index > -1){ + + if($scope.selected._id !== FORM_ACTION_ID){ + var currField = $scope.myform.visible_form_fields[$scope.selected.index]; + + //Jump to logicJump's destination if it is true + if(currField.logicJump && currField.logicJump.jumpTo && evaluateLogicJump(currField)){ + $scope.setActiveField(currField.logicJump.jumpTo, null, true); + } else if($scope.selected.index < $scope.myform.visible_form_fields.length-1){ + $scope.setActiveField(null, $scope.selected.index+1, true); + } else { + $scope.setActiveField(FORM_ACTION_ID, null, true); + } + } else { + //If we are at the submit actions page, go to the first field + $rootScope.setActiveField(null, 0, true); + } + } else { + //If selected is not defined go to the first field + $rootScope.setActiveField(null, 0, true); + } + }; $rootScope.prevField = $scope.prevField = function(){ + console.log('prevField'); + console.log($scope.selected); + var selected_index = $scope.selected.index - 1; 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); + $scope.setActiveField(null, selected_index, true); } }; + $rootScope.goToInvalid = $scope.goToInvalid = function() { + var field_id = $('.row.field-directive .ng-invalid.focusOn, .row.field-directive .ng-untouched.focusOn:not(.ng-valid)').first().parents('.row.field-directive').first().attr('data-id'); + $scope.setActiveField(field_id, null, true); + }; + /* ** Form Display Functions */ @@ -276,10 +306,6 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun } }; - $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'; @@ -320,6 +346,10 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun }; $rootScope.submitForm = $scope.submitForm = function() { + if($scope.forms.myForm.$invalid){ + $scope.goToInvalid(); + return; + } var _timeElapsed = TimeCounter.stopClock(); $scope.loading = true; diff --git a/public/form_modules/forms/base/views/directiveViews/field/date.html b/public/form_modules/forms/base/views/directiveViews/field/date.html index c2d74a20..bb8a9df6 100755 --- a/public/form_modules/forms/base/views/directiveViews/field/date.html +++ b/public/form_modules/forms/base/views/directiveViews/field/date.html @@ -16,6 +16,7 @@
{{field.description}}


-