Merge branch 'master' into master

This commit is contained in:
Varun 2017-10-07 19:06:40 +05:30 committed by GitHub
commit 248d0cd155
42 changed files with 999 additions and 671 deletions

View file

@ -1,8 +1,8 @@
TellForm 2.0.0 TellForm 2.1.0
======== ========
[![Build Status](https://travis-ci.org/tellform/tellform.svg?branch=master)](https://travis-ci.org/tellform/tellform) [![Build Status](https://travis-ci.org/tellform/tellform.svg?branch=master)](https://travis-ci.org/tellform/tellform)
![Project Status](https://img.shields.io/badge/status-2.0.0-green.svg) ![Project Status](https://img.shields.io/badge/status-2.1.0-green.svg)
[![Code Climate](https://codeclimate.com/github/whitef0x0/tellform/badges/gpa.svg)](https://codeclimate.com/github/whitef0x0/tellform) [![Code Climate](https://codeclimate.com/github/whitef0x0/tellform/badges/gpa.svg)](https://codeclimate.com/github/whitef0x0/tellform)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/3491e86eb7194308b8fc80711d736ede)](https://www.codacy.com/app/david-baldwin/tellform?utm_source=github.com&utm_medium=referral&utm_content=whitef0x0/tellform&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/3491e86eb7194308b8fc80711d736ede)](https://www.codacy.com/app/david-baldwin/tellform?utm_source=github.com&utm_medium=referral&utm_content=whitef0x0/tellform&utm_campaign=Badge_Grade)
[![Gitter](https://badges.gitter.im/whitef0x0/tellform.svg)](https://gitter.im/whitef0x0/tellform?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Gitter](https://badges.gitter.im/whitef0x0/tellform.svg)](https://gitter.im/whitef0x0/tellform?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

View file

@ -15,7 +15,7 @@ exports.index = function(req, res) {
exports.form = function(req, res) { exports.form = function(req, res) {
//Allow form to be embedded //Allow form to be embedded
res.set('X-Frame-Options', 'GOFORIT'); res.removeHeader('X-Frame-Options');
res.render('form', { res.render('form', {
user: req.user || null, user: req.user || null,

View file

@ -164,7 +164,7 @@ var readForRender = exports.readForRender = function(req, res) {
delete newForm.__v; delete newForm.__v;
delete newForm.created; delete newForm.created;
if(!newForm.startPage.showStart){ if(newForm.startPage && !newForm.startPage.showStart){
delete newForm.startPage; delete newForm.startPage;
} }
@ -175,20 +175,33 @@ var readForRender = exports.readForRender = function(req, res) {
* Update a form * Update a form
*/ */
exports.update = function(req, res) { exports.update = function(req, res) {
var form = req.form; var form = req.form;
var updatedForm = req.body.form; var updatedForm = req.body.form;
if(form.form_fields === undefined){
form.form_fields = [];
}
delete updatedForm.__v; delete updatedForm.__v;
delete updatedForm.created; delete updatedForm.created;
if(form.analytics === undefined){
form.analytics = {
visitors: [],
gaCode: ''
}
}
if (req.body.changes) { if (req.body.changes) {
var formChanges = req.body.changes; var formChanges = req.body.changes;
formChanges.forEach(function (change) { formChanges.forEach(function (change) {
diff.applyChange(form, true, change); diff.applyChange(form._doc, true, change);
}); });
} else { } 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) { if(updatedForm && req.user.roles.indexOf('admin') === -1) {
delete updatedForm.admin; delete updatedForm.admin;
} }
@ -200,7 +213,7 @@ exports.update = function(req, res) {
//Do this so we can create duplicate fields //Do this so we can create duplicate fields
var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$'); var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$');
for(var i=0; i<req.body.form.form_fields.length; i++){ for(var i=0; i < req.body.form.form_fields.length; i++){
var field = req.body.form.form_fields[i]; var field = req.body.form.form_fields[i];
if(!checkForValidId.exec(field._id+'')){ if(!checkForValidId.exec(field._id+'')){
delete field._id; delete field._id;
@ -311,7 +324,6 @@ exports.formByIDFast = function(req, res, next, id) {
} }
Form.findById(id) Form.findById(id)
.lean() .lean()
.cache()
.select('title language form_fields startPage endPage hideFooter isLive design analytics.gaCode') .select('title language form_fields startPage endPage hideFooter isLive design analytics.gaCode')
.exec(function(err, form) { .exec(function(err, form) {
if (err) { if (err) {

View file

@ -8,7 +8,16 @@ var errorHandler = require('../errors.server.controller'),
passport = require('passport'), passport = require('passport'),
config = require('../../../config/config'), config = require('../../../config/config'),
User = mongoose.model('User'), User = mongoose.model('User'),
tokgen = require('../../libs/tokenGenerator'); tokgen = require('../../libs/tokenGenerator'),
fs = require('fs');
require.extensions['.html'] = function (module, filename) {
module.exports = fs.readFileSync(filename, 'utf8');
};
var welcomeEmail = require("../../views/welcome.email.view.html");
var verificationEmail = require("../../views/verification.email.view.html");
var nev = require('email-verification')(mongoose); var nev = require('email-verification')(mongoose);
@ -26,16 +35,15 @@ var config_nev = function () {
transportOptions: config.mailer.options, transportOptions: config.mailer.options,
verifyMailOptions: { verifyMailOptions: {
from: config.mailer.from, from: config.mailer.from,
subject: 'Confirm your account', subject: '✔ Activate your new TellForm account!',
html: '<p>Please verify your account by clicking <a href="http://${URL}">this link</a>. If you are unable to do so, copy and ' + html: welcomeEmail,
'paste the following link into your browser:</p><p>${URL}</p>',
text: 'Please verify your account by clicking the following link, or by copying and pasting it into your browser: ${URL}' text: 'Please verify your account by clicking the following link, or by copying and pasting it into your browser: ${URL}'
}, },
confirmMailOptions: { confirmMailOptions: {
from: config.mailer.from, from: config.mailer.from,
subject: 'Account successfully verified!', subject: '✔ Welcome to {{app.title}}!',
html: '<p>Your account has been successfully verified.</p>', html: verificationEmail,
text: 'Your account has been successfully verified.' text: 'Your account has been successfully verified.'
}, },
verifySendMailCallback: function(err, info) { verifySendMailCallback: function(err, info) {

View file

@ -76,8 +76,7 @@ function BaseFieldSchema(){
}, },
title: { title: {
type: String, type: String,
trim: true, trim: true
required: 'Field Title cannot be blank'
}, },
description: { description: {
type: String, type: String,
@ -106,7 +105,6 @@ function BaseFieldSchema(){
}, },
fieldType: { fieldType: {
type: String, type: String,
required: true,
enum: [ enum: [
'textfield', 'textfield',
'date', 'date',

View file

@ -5,13 +5,14 @@
*/ */
var mongoose = require('mongoose'), var mongoose = require('mongoose'),
errorHandler = require('../controllers/errors.server.controller'), errorHandler = require('../controllers/errors.server.controller'),
Form = mongoose.model('Form'); Form = mongoose.model('Form'),
request = require('request');
// Create the chat configuration // Create the chat configuration
module.exports = function (io, socket) { module.exports = function (io, socket) {
var visitorsData = {}; var visitorsData = {};
var saveVisitorData = function (data, cb){ var saveVisitorData = function (data, socket, cb){
Form.findById(data.formId, function(err, form) { Form.findById(data.formId, function(err, form) {
if (err) { if (err) {
console.error(err); console.error(err);
@ -25,12 +26,15 @@ module.exports = function (io, socket) {
timeElapsed: data.timeElapsed, timeElapsed: data.timeElapsed,
isSubmitted: data.isSubmitted, isSubmitted: data.isSubmitted,
language: data.language, language: data.language,
ipAddr: data.ipAddr, ipAddr: '',
deviceType: data.deviceType deviceType: data.deviceType
}; };
form.analytics.visitors.push(newVisitor); form.analytics.visitors.push(newVisitor);
form.form_fields = form.form_fields.map(v => Object.assign({}, v, { fieldValue: null }));
form.save(function (formSaveErr) { form.save(function (formSaveErr) {
if (err) { if (err) {
console.error(err); console.error(err);
@ -50,6 +54,8 @@ module.exports = function (io, socket) {
visitorsData[current_socket.id] = data; visitorsData[current_socket.id] = data;
visitorsData[current_socket.id].socketId = current_socket.id; visitorsData[current_socket.id].socketId = current_socket.id;
visitorsData[current_socket.id].isSaved = false; visitorsData[current_socket.id].isSaved = false;
if (data.isSubmitted && !data.isSaved) { if (data.isSubmitted && !data.isSaved) {
visitorsData[current_socket.id].isSaved = true; visitorsData[current_socket.id].isSaved = true;
saveVisitorData(data, function() { saveVisitorData(data, function() {
@ -71,3 +77,4 @@ module.exports = function (io, socket) {
}); });
}); });
}; };

View file

@ -76,11 +76,6 @@
<script src="/static/lib/jquery/dist/jquery.min.js" type="text/javascript"></script> <script src="/static/lib/jquery/dist/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(window).on("load", function() {
$(".loader").fadeOut("slow");
});
</script>
<link rel="stylesheet" href="/static/lib/font-awesome/css/font-awesome.min.css"> <link rel="stylesheet" href="/static/lib/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="/static/lib/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/lib/bootstrap/dist/css/bootstrap.min.css">

View file

@ -1,13 +1,64 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <html>
<head> <head>@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&subset=latin,latin-ext);
</head> <style>
<body> a {
<p>Dear {{name}},</p> color: #007ee6;
<p></p> text-decoration: none;
<p>This is a confirmation that the password for your account has just been changed</p> }
<br> </style>
<br> <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<p>The {{appName}} Support Team</p> </head>
</body> <body style="padding: 0; width: 100% !important; -webkit-text-size-adjust: 100%; margin: 0; -ms-text-size-adjust: 100%;" marginheight="0" marginwidth="0">
<center>
<table cellpadding="8" cellspacing="0" style="*width: 540px; padding: 0; width: 100% !important; background: #ffffff; margin: 0; background-color: #ffffff;" border="0">
<tr>
<td valign="top">
<table cellpadding="0" cellspacing="0" style="border-radius: 6px; -webkit-border-radius: 6px; border: 1px #c0c0c0 solid; -moz-border-radius: 6px;" border="0" align="center">
<tr>
<td colspan="3" height="6"></td>
</tr>
<tr>
<td>
<table cellpadding="0" cellspacing="0" style="line-height: 25px;" border="0" align="center">
<tr>
<td colspan="3" height="30"></td>
</tr>
<tr>
<td width="36"></td>
<td width="454" align="left" style="color: #444444; border-collapse: collapse; font-size: 11pt; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; max-width: 454px;" valign="top">
<p>Hello there!</p>
<p>This is a courtesy message to confirm that your password was just changed.</p>
<p>Thanks so much for using our services! If you have any questions, or suggestions, please feel free to email us here at&nbsp;<a href="mailto:team@tellform.com">team@tellform.com</a>.</p>
<p> - The {{appName}} team</p>
</td>
<td width="36"></td>
</tr>
<tr>
<td colspan="3" height="36"></td>
</tr>
</table>
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr>
<td height="10"></td>
</tr>
<tr>
<td style="padding: 0; border-collapse: collapse;">
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr style="color: #c0c0c0; font-size: 11px; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; -webkit-text-size-adjust: none;">
<td width="400" align="left"></td>
<td width="128" align="right">© TellForm 2017</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</body>
</html> </html>

View file

@ -1,18 +1,66 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <html>
<head> <head>@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&subset=latin,latin-ext);
</head> <style>
<body> a {
<p>Dear {{name}},</p> color: #007ee6;
<br> text-decoration: none;
<p> }
You have requested to have your password reset for your account at {{appName}} </style>
</p> <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<p>Please visit this url to reset your password:</p> </head>
<p>{{url}}</p> <body style="padding: 0; width: 100% !important; -webkit-text-size-adjust: 100%; margin: 0; -ms-text-size-adjust: 100%;" marginheight="0" marginwidth="0">
<strong>If you didn't make this request, you can ignore this email.</strong> <center>
<br> <table cellpadding="8" cellspacing="0" style="*width: 540px; padding: 0; width: 100% !important; background: #ffffff; margin: 0; background-color: #ffffff;" border="0">
<br> <tr>
<p>The {{appName}} Support Team</p> <td valign="top">
</body> <table cellpadding="0" cellspacing="0" style="border-radius: 6px; -webkit-border-radius: 6px; border: 1px #c0c0c0 solid; -moz-border-radius: 6px;" border="0" align="center">
<tr>
<td colspan="3" height="6"></td>
</tr>
<tr>
<td>
<table cellpadding="0" cellspacing="0" style="line-height: 25px;" border="0" align="center">
<tr>
<td colspan="3" height="30"></td>
</tr>
<tr>
<td width="36"></td>
<td width="454" align="left" style="color: #444444; border-collapse: collapse; font-size: 11pt; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; max-width: 454px;" valign="top">
<p>Hello there!</p>
<p>Here is a special link that will allow you to reset your password. Please note it will expire in one hour for your protection:</p>
<p><a href="{{url}}">Reset Your Password</a></p>
<p>If you did not request this, please ignore this email and your password will remain unchanged.</p>
<p>Thanks so much for using our services! If you have any questions, or suggestions, please feel free to email us here at&nbsp;<a href="mailto:team@tellform.com">team@tellform.com</a>.</p>
<p> - The {{appName}} team</p>
</td>
<td width="36"></td>
</tr>
<tr>
<td colspan="3" height="36"></td>
</tr>
</table>
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr>
<td height="10"></td>
</tr>
<tr>
<td style="padding: 0; border-collapse: collapse;">
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr style="color: #c0c0c0; font-size: 11px; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; -webkit-text-size-adjust: none;">
<td width="400" align="left"></td>
<td width="128" align="right">© TellForm 2017</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</body>
</html> </html>

View file

@ -1,71 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
</head>
<body style="padding: 0; width: 100% !important; -webkit-text-size-adjust: 100%; margin: 0; -ms-text-size-adjust: 100%;" marginheight="0" marginwidth="0">
<center>
<table cellpadding="8" cellspacing="0" style="*width: 540px; padding: 0; width: 100% !important; background: #ffffff; margin: 0; background-color: #ffffff;" border="0">
<tr>
<td valign="top">
<table cellpadding="0" cellspacing="0" style="border-radius: 6px; -webkit-border-radius: 6px; border: 1px #c0c0c0 solid; -moz-border-radius: 6px;" border="0" align="center">
<tr>
<td colspan="3" height="6"></td>
</tr>
<tr>
<td>
<table cellpadding="0" cellspacing="0" style="line-height: 25px;" border="0" align="center">
<tr>
<td colspan="3" height="30"></td>
</tr>
<tr>
<td width="36"></td>
<td width="454" align="left" style="color: #444444; border-collapse: collapse; font-size: 11pt; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; max-width: 454px;" valign="top">
//- -----------------------------------------------------------------------------------
//- Only Edit Here
//- -----------------------------------------------------------------------------------
p Hello {{name}}!
p
| Welcome to {{appName}}! Here is a special link to activate your new account:
p
a(href='${URL}') Activate my account
p
| Thanks so much for using our services! If you have any questions, or suggestions, please feel free to email us here at&nbsp;
a(href='mailto:{{contactEmail}}') {{contactEmail}}
| .
p   - The {{appName}} team
//- ----------------------------------------------------------------------------------
</td>
<td width="36"></td>
</tr>
<tr>
<td colspan="3" height="36"></td>
</tr>
</table>
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr>
<td height="10"></td>
</tr>
<tr>
<td style="padding: 0; border-collapse: collapse;">
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr style="color: #c0c0c0; font-size: 11px; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; -webkit-text-size-adjust: none;"></tr>
<td width="400" align="left">
<td width="128" align="right">© </td>
</td>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<head>
@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&subset=latin,latin-ext);
<style>
a {
color: #007ee6;
text-decoration: none;
}
</style>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<body style="padding: 0; width: 100% !important; -webkit-text-size-adjust: 100%; margin: 0; -ms-text-size-adjust: 100%;" marginheight="0" marginwidth="0">
<center>
<table cellpadding="8" cellspacing="0" style="*width: 540px; padding: 0; width: 100% !important; background: #ffffff; margin: 0; background-color: #ffffff;" border="0">
<tr>
<td valign="top">
<table cellpadding="0" cellspacing="0" style="border-radius: 6px; -webkit-border-radius: 6px; border: 1px #c0c0c0 solid; -moz-border-radius: 6px;" border="0" align="center">
<tr>
<td colspan="3" height="6"></td>
</tr>
<tr>
<td>
<table cellpadding="0" cellspacing="0" style="line-height: 25px;" border="0" align="center">
<tr>
<td colspan="3" height="30"></td>
</tr>
<tr>
<td width="36"></td>
<td width="454" align="left" style="color: #444444; border-collapse: collapse; font-size: 11pt; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; max-width: 454px;" valign="top">
<p>Hello there!</p>
<p>Welcome to TellForm! Here is a special link to activate your new account:</p>
<p><a href="${URL}">Activate my account</a></p>
<p>Thanks so much for using our services! If you have any questions, or suggestions, please feel free to email us here at&nbsp;<a href="mailto:team@tellform.com">team@tellform.com</a>.</p>
<p> - The TellForm team</p>
</td>
<td width="36"></td>
</tr>
<tr>
<td colspan="3" height="36"></td>
</tr>
</table>
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr>
<td height="10"></td>
</tr>
<tr>
<td style="padding: 0; border-collapse: collapse;">
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr style="color: #c0c0c0; font-size: 11px; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; -webkit-text-size-adjust: none;">
<td width="400" align="left"></td>
<td width="128" align="right">© TellForm 2017</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</body>
</head>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&subset=latin,latin-ext);
<style>
a {
color: #007ee6;
text-decoration: none;
}
</style>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body style="padding: 0; width: 100% !important; -webkit-text-size-adjust: 100%; margin: 0; -ms-text-size-adjust: 100%;" marginheight="0" marginwidth="0">
<center>
<table cellpadding="8" cellspacing="0" style="*width: 540px; padding: 0; width: 100% !important; background: #ffffff; margin: 0; background-color: #ffffff;" border="0">
<tr>
<td valign="top">
<table cellpadding="0" cellspacing="0" style="border-radius: 6px; -webkit-border-radius: 6px; border: 1px #c0c0c0 solid; -moz-border-radius: 6px;" border="0" align="center">
<tr>
<td colspan="3" height="6"></td>
</tr>
<tr>
<td>
<table cellpadding="0" cellspacing="0" style="line-height: 25px;" border="0" align="center">
<tr>
<td colspan="3" height="30"></td>
</tr>
<tr>
<td width="36"></td>
<td width="454" align="left" style="color: #444444; border-collapse: collapse; font-size: 11pt; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; max-width: 454px;" valign="top">
<p>Hello there!</p>
<p>We would like to welcome you as our newest member!</p>
<p>Thanks so much for using TellForm! If you have any questions, or suggestions, please feel free to email us here at&nbsp;<a href="mailto:team@tellform.com">team@tellform.com</a>.</p>
<p> - The TellForm team</p>
</td>
<td width="36"></td>
</tr>
<tr>
<td colspan="3" height="36"></td>
</tr>
</table>
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr>
<td height="10"></td>
</tr>
<tr>
<td style="padding: 0; border-collapse: collapse;">
<table cellpadding="0" cellspacing="0" align="center" border="0">
<tr style="color: #c0c0c0; font-size: 11px; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; -webkit-text-size-adjust: none;">
<td width="400" align="left"></td>
<td width="128" align="right">© TellForm 2017</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</body>
</html>

View file

@ -1,7 +1,7 @@
{ {
"name": "TellForm", "name": "TellForm",
"description": "Opensource alternative to TypeForm", "description": "Opensource alternative to TypeForm",
"version": "2.0.0", "version": "2.1.0",
"homepage": "https://github.com/whitef0x0/tellform", "homepage": "https://github.com/whitef0x0/tellform",
"authors": [ "authors": [
"David Baldwynn <polydaic@gmail.com> (http://baldwynn.me)" "David Baldwynn <polydaic@gmail.com> (http://baldwynn.me)"
@ -95,6 +95,7 @@
"random-js": "^1.0.8", "random-js": "^1.0.8",
"raven": "^0.9.0", "raven": "^0.9.0",
"shortid": "^2.2.8", "shortid": "^2.2.8",
"request": "^2.83.0",
"socket.io": "^1.4.6", "socket.io": "^1.4.6",
"socket.io-redis": "^1.0.0", "socket.io-redis": "^1.0.0",
"swig": "~1.4.1", "swig": "~1.4.1",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -5,6 +5,9 @@ angular.module('view-form').controller('SubmitFormController', [
'$scope', '$rootScope', '$state', '$translate', 'myForm', '$scope', '$rootScope', '$state', '$translate', 'myForm',
function($scope, $rootScope, $state, $translate, myForm) { function($scope, $rootScope, $state, $translate, myForm) {
$scope.myform = myForm; $scope.myform = myForm;
$(".loader").fadeOut("slow");
document.body.style.background = myForm.design.colors.backgroundColor;
$translate.use(myForm.language); $translate.use(myForm.language);
} }
]); ]);

View file

@ -25,7 +25,7 @@ angular.module('view-form').directive('onEnterKey', ['$rootScope', function($roo
return { return {
restrict: 'A', restrict: 'A',
link: function($scope, $element, $attrs) { link: function($scope, $element, $attrs) {
$element.bind('keydown keypress', function(event) { $element.bind('keyup keypress', function(event) {
var keyCode = event.which || event.keyCode; 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; var keyCode = event.which || event.keyCode;
if(keyCode === 9 && event.shiftKey) { if(keyCode === 9 && event.shiftKey) {
console.log('onTabAndShiftKey');
event.preventDefault(); event.preventDefault();
$rootScope.$apply(function() { $rootScope.$apply(function() {
$rootScope.$eval($attrs.onTabAndShiftKey); $rootScope.$eval($attrs.onTabAndShiftKey);

View file

@ -9,8 +9,8 @@ jsep.addBinaryOp('!begins', 10);
jsep.addBinaryOp('ends', 10); jsep.addBinaryOp('ends', 10);
jsep.addBinaryOp('!ends', 10); jsep.addBinaryOp('!ends', 10);
angular.module('view-form').directive('submitFormDirective', ['$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) { function ($http, TimeCounter, $filter, $rootScope, SendVisitorData, $translate, $timeout) {
return { return {
templateUrl: 'form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html', templateUrl: 'form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html',
restrict: 'E', restrict: 'E',
@ -19,7 +19,8 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
ispreview: '=' ispreview: '='
}, },
controller: function($document, $window, $scope){ controller: function($document, $window, $scope){
$scope.noscroll = false; var NOSCROLL = false;
var FORM_ACTION_ID = 'submit_field';
$scope.forms = {}; $scope.forms = {};
//Don't start timer if we are looking at a design preview //Don't start timer if we are looking at a design preview
@ -57,43 +58,6 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
//Reset Timer //Reset Timer
TimeCounter.restartClock(); 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();
}
}; };
/* /*
@ -163,35 +127,46 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
throw new Error('current active field is null'); 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.myform.form_fields.length - 1;
} }
return $scope.selected.index; 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) { $scope.setActiveField = $rootScope.setActiveField = function(field_id, field_index, animateScroll) {
if($scope.selected === null || $scope.selected._id === field_id){ if($scope.selected === null || (!field_id && field_index === null) ) {
//console.log('not scrolling');
//console.log($scope.selected);
return; return;
} }
//console.log('field_id: '+field_id);
//console.log('field_index: '+field_index);
//console.log($scope.selected);
$scope.selected._id = field_id; if(!field_id){
$scope.selected.index = field_index; field_id = $scope.myform.visible_form_fields[field_index]._id;
if(!field_index){ } else if(field_index === null){
for(var i=0; i<$scope.myform.visible_form_fields.length; i++){ field_index = $scope.myform.visible_form_fields.length
for(var i=0; i < $scope.myform.visible_form_fields.length; i++){
var currField = $scope.myform.visible_form_fields[i]; var currField = $scope.myform.visible_form_fields[i];
if(field_id === currField._id){ if(currField['_id'] == field_id){
$scope.selected.index = i; field_index = i;
break; 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); var nb_valid = $filter('formValidity')($scope.myform);
$scope.translateAdvancementData = { $scope.translateAdvancementData = {
done: nb_valid, done: nb_valid,
@ -200,10 +175,10 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
}; };
if(animateScroll){ if(animateScroll){
$scope.noscroll=true; NOSCROLL=true;
setTimeout(function() { setTimeout(function() {
$document.scrollToElement(angular.element('.activeField'), -10, 200).then(function() { $document.scrollToElement(angular.element('.activeField'), -10, 200).then(function() {
$scope.noscroll = false; NOSCROLL = false;
setTimeout(function() { setTimeout(function() {
if (document.querySelectorAll('.activeField .focusOn').length) { if (document.querySelectorAll('.activeField .focusOn').length) {
//Handle default case //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());
} }
}; };
$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(){ $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]; 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 //Jump to logicJump's destination if it is true
if(currField.logicJump && evaluateLogicJump(currField)){ if(currField.logicJump && currField.logicJump.jumpTo && evaluateLogicJump(currField)){
$rootScope.setActiveField(currField.logicJump.jumpTo, null, true); $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 { } else {
var selected_index, selected_id; $scope.setActiveField(FORM_ACTION_ID, null, true);
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);
} }
} 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(){ $rootScope.prevField = $scope.prevField = function(){
if($scope.selected.index > 0){ console.log('prevField');
console.log($scope.selected);
var selected_index = $scope.selected.index - 1; var selected_index = $scope.selected.index - 1;
var selected_id = $scope.myform.visible_form_fields[selected_index]._id; if($scope.selected.index > 0){
$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 ** 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 getDeviceData = function(){
var md = new MobileDetect(window.navigator.userAgent); var md = new MobileDetect(window.navigator.userAgent);
var deviceType = 'other'; var deviceType = 'other';
@ -320,6 +346,10 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
}; };
$rootScope.submitForm = $scope.submitForm = function() { $rootScope.submitForm = $scope.submitForm = function() {
if($scope.forms.myForm.$invalid){
$scope.goToInvalid();
return;
}
var _timeElapsed = TimeCounter.stopClock(); var _timeElapsed = TimeCounter.stopClock();
$scope.loading = true; $scope.loading = true;

View file

@ -16,6 +16,7 @@
<div class="col-xs-12 field-input"> <div class="col-xs-12 field-input">
<div class="control-group input-append"> <div class="control-group input-append">
<input class="focusOn" <input class="focusOn"
ng-focus="setActiveField(field._id, null, false)"
ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}" ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}"
ng-class="{ 'no-border': !!field.fieldValue }" ng-class="{ 'no-border': !!field.fieldValue }"
ui-date="dateOptions" ui-date="dateOptions"

View file

@ -15,6 +15,7 @@
</div> </div>
<div class="col-xs-12 field-input"> <div class="col-xs-12 field-input">
<ui-select ng-model="field.fieldValue" <ui-select ng-model="field.fieldValue"
ng-focus="setActiveField(field._id, null, false)"
theme="selectize" theme="selectize"
search-enabled="true" search-enabled="true"
search-by="option_value" search-by="option_value"

View file

@ -26,6 +26,7 @@
{{$index+1}} {{$index+1}}
</div> </div>
<input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}" <input ng-style="{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}"
ng-focus="setActiveField(field._id, null, false)"
type="radio" class="focusOn" type="radio" class="focusOn"
value="{{option.option_value}}" value="{{option.option_value}}"
ng-model="field.fieldValue" ng-model="field.fieldValue"

View file

@ -12,7 +12,7 @@
<p class="col-xs-12" ng-if="field.description.length">{{field.description}} </p> <p class="col-xs-12" ng-if="field.description.length">{{field.description}} </p>
<br> <br>
<div class="col-xs-offset-1 col-xs-11"> <div class="col-xs-offset-1 col-xs-11">
<button class="btn focusOn"> <button class="btn focusOn"
ng-style="{'font-size': '1.3em', 'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}" ng-style="{'font-size': '1.3em', 'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
ng-click="nextField()"> ng-click="nextField()">
{{ 'CONTINUE' | translate }} {{ 'CONTINUE' | translate }}

View file

@ -16,6 +16,7 @@
<div class="col-xs-12 field-input"> <div class="col-xs-12 field-input">
<small style="font-size:0.6em;">Press SHIFT+ENTER to add a newline</small> <small style="font-size:0.6em;">Press SHIFT+ENTER to add a newline</small>
<textarea class="textarea focusOn" type="text" <textarea class="textarea focusOn" type="text"
ng-focus="setActiveField(field._id, null, false)"
ng-model="field.fieldValue" ng-model="field.fieldValue"
ng-model-options="{ debounce: 250 }" ng-model-options="{ debounce: 250 }"
ng-class="{ 'no-border': !!field.fieldValue }" ng-class="{ 'no-border': !!field.fieldValue }"

View file

@ -26,6 +26,7 @@
placeholder="{{placeholder}}" placeholder="{{placeholder}}"
ng-class="{ 'no-border': !!field.fieldValue }" ng-class="{ 'no-border': !!field.fieldValue }"
class="focusOn text-field-input" class="focusOn text-field-input"
ng-focus="setActiveField(field._id, null, false)"
ng-model="field.fieldValue" ng-model="field.fieldValue"
ng-model-options="{ debounce: 250 }" ng-model-options="{ debounce: 250 }"
value="field.fieldValue" value="field.fieldValue"
@ -45,7 +46,7 @@
</div> </div>
</div> </div>
<div> <div>
<div class="btn btn-lg btn-default" <div class="btn btn-lg btn-default" ng-disabled="!field.fieldValue || field.$invalid"
style="padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)"> style="padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)">
<button ng-disabled="!field.fieldValue || field.$invalid" <button ng-disabled="!field.fieldValue || field.$invalid"
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}" ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"

View file

@ -28,6 +28,7 @@
<input type="radio" value="true" <input type="radio" value="true"
class="focusOn" class="focusOn"
style="opacity: 0; margin-left: 0px;" style="opacity: 0; margin-left: 0px;"
ng-focus="setActiveField(field._id, null, false)"
ng-model="field.fieldValue" ng-model="field.fieldValue"
ng-model-options="{ debounce: 250 }" ng-model-options="{ debounce: 250 }"
ng-required="field.required" ng-required="field.required"
@ -46,6 +47,7 @@
<input type="radio" value="false" <input type="radio" value="false"
style="opacity:0; margin-left:0px;" style="opacity:0; margin-left:0px;"
ng-focus="setActiveField(field._id, null, false)"
ng-model="field.fieldValue" ng-model="field.fieldValue"
ng-model-options="{ debounce: 250 }" ng-model-options="{ debounce: 250 }"
ng-required="field.required" ng-required="field.required"

View file

@ -1,16 +1,13 @@
<section class="overlay submitform" ng-if="!ispreview && (loading || (!myform.submitted && !myform.startPage.showStart))"></section>
<section class="overlay previewform submitform" ng-if="ispreview && (loading || (!myform.submitted && !myform.startPage.showStart))"></section>
<!-- Start Page View --> <!-- Start Page View -->
<div ng-show="!myform.submitted && myform.startPage.showStart" class="form-submitted" style="padding-top: 35vh;"> <div ng-show="!myform.submitted && myform.startPage.showStart" class="form-submitted" style="padding-top: 35vh;">
<div class="row"> <div class="row">
<div class="col-xs-12 text-center" style="overflow-wrap: break-word;"> <div class="col-xs-12 text-center" style="overflow-wrap: break-word;">
<h1 style="font-weight: 400; nont-size: 25px;" ng-style="{'color': form.design.colors.questionColor}"> <h1 style="font-weight: 400; nont-size: 25px;" ng-style="{'color': myform.design.colors.questionColor}">
{{myform.startPage.introTitle}} {{myform.startPage.introTitle}}
</h1> </h1>
</div> </div>
<div class="col-xs-10 col-xs-offset-1 text-center" style="overflow-wrap: break-word;"> <div class="col-xs-10 col-xs-offset-1 text-center" style="overflow-wrap: break-word;">
<p style="font-weight: 100; font-size: 16px;" ng-style="{'color': form.design.colors.questionColor}"> <p style="font-weight: 100; font-size: 16px;" ng-style="{'color': myform.design.colors.questionColor}">
{{myform.startPage.introParagraph}} {{myform.startPage.introParagraph}}
</p> </p>
</div> </div>
@ -37,27 +34,29 @@
</div> </div>
<!-- Form Fields View --> <!-- Form Fields View -->
<div class="form-fields" ng-show="!myform.submitted && !myform.startPage.showStart" ng-style="{ 'border-color': myform.design.colors.buttonTextColor }"> <div class="form-fields"
ng-show="!myform.submitted && !myform.startPage.showStart"
ng-style="{ 'border-color': myform.design.colors.buttonTextColor }">
<div class="row"> <div class="row form-field-wrapper">
<form name="forms.myForm" novalidate class="submission-form col-sm-12 col-md-offset-1 col-md-10"> <form name="forms.myForm" novalidate class="submission-form">
<div ng-repeat="field in myform.form_fields" ng-if="!field.deletePreserved" data-index="{{$index}}" data-id="{{field._id}}" ng-class="{activeField: selected._id == field._id }" class="row field-directive"> <div ng-repeat="field in myform.form_fields" ng-if="!field.deletePreserved" data-index="{{$index}}" data-id="{{field._id}}" ng-class="{activeField: selected._id == field._id }" class="row field-directive">
<field-directive field="field" design="myform.design" index="$index" forms="forms"> <field-directive field="field" design="myform.design" index="$index" forms="forms">
</field-directive> </field-directive>
</div> </div>
<div class="row form-actions" id="submit_field" ng-class="{activeField: selected._id == 'submit_field' }"
</form> ng-style="{ 'background-color':myform.design.colors.buttonColor}" style="border-top: 1px solid #ddd; margin-top: 30vh; height: 100vh; margin-left: 1%; margin-right: 1%;"
</div> on-tab-and-shift-key="prevField()"
on-tab-key="nextField()"
<div class="row form-actions" id="submit_field" ng-class="{activeField: selected._id == 'submit_field' }" ng-style="{ 'background-color':myform.design.colors.buttonColor}" style="border-top: 1px solid #ddd; margin-right: -13%; margin-left: -13%; margin-top: 30vh; height: 100vh"> on-enter-key="submitForm()">
<div class="col-xs-12 text-left" style="background-color:#990000; color:white;" ng-if="forms.myForm.$invalid"> <div class="col-xs-12 text-left" style="background-color:#990000; color:white;" ng-if="forms.myForm.$invalid">
{{ 'COMPLETING_NEEDED' | translate:translateAdvancementData }} {{ 'COMPLETING_NEEDED' | translate:translateAdvancementData }}
</div> </div>
<button ng-if="!forms.myForm.$invalid" class="Button btn col-sm-2 col-xs-8 focusOn" v-busy="loading" v-busy-label="Please wait" v-pressable ng-disabled="loading || forms.myForm.$invalid" ng-click="submitForm()" on-enter-key="submitForm()" on-enter-key-disabled="loading || forms.myForm.$invalid" ng-style="{'background-color':myform.design.colors.buttonColor, 'color':myform.design.colors.buttonTextColor}" style="font-size: 1.6em; margin-left: 1em; margin-top: 1em;"> <button ng-if="!forms.myForm.$invalid" class="Button btn col-sm-2 col-xs-8 focusOn" v-busy="loading" v-busy-label="Please wait" v-pressable ng-disabled="loading || forms.myForm.$invalid" ng-click="submitForm()" on-enter-key-disabled="loading || forms.myForm.$invalid" ng-style="{'background-color':myform.design.colors.buttonColor, 'color':myform.design.colors.buttonTextColor}" style="font-size: 1.6em; margin-left: 1em; margin-top: 1em;">
{{ 'SUBMIT' | translate }} {{ 'SUBMIT' | translate }}
</button> </button>
@ -72,6 +71,8 @@
</small> </small>
</div> </div>
</div> </div>
</form>
</div>
<section ng-if="!myform.hideFooter" class="navbar navbar-fixed-bottom" ng-style="{ 'background-color':myform.design.colors.buttonColor, 'padding-top': '15px', 'border-top': '2px '+ myform.design.colors.buttonTextColor +' solid', 'color':myform.design.colors.buttonTextColor}"> <section ng-if="!myform.hideFooter" class="navbar navbar-fixed-bottom" ng-style="{ 'background-color':myform.design.colors.buttonColor, 'padding-top': '15px', 'border-top': '2px '+ myform.design.colors.buttonTextColor +' solid', 'color':myform.design.colors.buttonTextColor}">
<div class="container-fluid"> <div class="container-fluid">

View file

@ -0,0 +1,12 @@
<script>
$(".loader").fadeOut("slow");
</script>
<section class="public-form auth sigin-view valign-wrapper">
<div class="row valign">
<h3 class="col-md-12 text-center">404 - Form Does not Exist </h3>
<div class="col-md-4 col-md-offset-4">
<div class="col-md-12 text-center" style="padding-bottom: 50px;">
The form you are trying to access does not exist. Sorry about that!
</div>
</div>
</section>

View file

@ -1,4 +1,7 @@
<section class="auth sigin-view valign-wrapper"> <script>
$(".loader").fadeOut("slow");
</script>
<section class="public-form auth sigin-view valign-wrapper">
<div class="row valign"> <div class="row valign">
<h3 class="col-md-12 text-center">Not Authorized to Access Form</h3> <h3 class="col-md-12 text-center">Not Authorized to Access Form</h3>
<div class="col-md-4 col-md-offset-4"> <div class="col-md-4 col-md-offset-4">

View file

@ -1,4 +1,4 @@
<section class="public-form" ng-style="{ 'background-color': myform.design.colors.backgroundColor }"> <section class="public-form">
<submit-form-directive myform="myform"></submit-form-directive> <submit-form-directive myform="myform"></submit-form-directive>
</section> </section>

View file

@ -27,7 +27,11 @@ angular.module('view-form').config(['$stateProvider',
}). }).
state('unauthorizedFormAccess', { state('unauthorizedFormAccess', {
url: '/forms/unauthorized', url: '/forms/unauthorized',
templateUrl: '/static/form_modules/forms/base/views/form-unauthorized.client.view.html', templateUrl: '/static/form_modules/forms/base/views/form-unauthorized.client.view.html'
})
.state('formNotFound', {
url: '*path',
templateUrl: '/static/form_modules/forms/base/views/form-not-found.client.view.html'
}); });
} }
]); ]);

View file

@ -28,18 +28,6 @@
deviceType = 'desktop'; 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 // Create a new message object
var visitorData = { var visitorData = {
referrer: document.referrer, referrer: document.referrer,
@ -49,11 +37,8 @@
timeElapsed: timeElapsed, timeElapsed: timeElapsed,
language: lang, language: lang,
deviceType: deviceType, deviceType: deviceType,
ipAddr: geoData.ip, ipAddr: null,
geoLocation: { geoLocation: null
city: geoData.city,
country: geoData.country_name
}
}; };
Socket.emit('form-visitor-data', visitorData); Socket.emit('form-visitor-data', visitorData);

View file

@ -16,6 +16,7 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$windo
$scope.animationsEnabled = true; $scope.animationsEnabled = true;
$scope.myform = myForm; $scope.myform = myForm;
$rootScope.saveInProgress = false; $rootScope.saveInProgress = false;
$scope.oldForm = _.cloneDeep($scope.myform);
CurrentForm.setForm($scope.myform); CurrentForm.setForm($scope.myform);
@ -49,6 +50,16 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$windo
} }
]; ];
$scope.designTabActive = false
$scope.deactivateDesignTab = function(){
$scope.designTabActive = false
}
$scope.activateDesignTab = function(){
$scope.designTabActive = true
}
$scope.setForm = function(form){ $scope.setForm = function(form){
$scope.myform = form; $scope.myform = form;
}; };
@ -102,16 +113,20 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$windo
} }
}; };
// Update existing Form $scope.updateDesign = function(updateImmediately, data, shouldDiff, refreshAfterUpdate){
$scope.update = $rootScope.update = function(updateImmediately, data, isDiffed, refreshAfterUpdate, cb){ $scope.update(updateImmediately, data, shouldDiff, refreshAfterUpdate, function(){
refreshFrame(); refreshFrame();
});
}
// Update existing Form
$scope.update = $rootScope.update = function(updateImmediately, data, shouldDiff, refreshAfterUpdate, cb){
var continueUpdate = true; var continueUpdate = true;
if(!updateImmediately){ if(!updateImmediately){
continueUpdate = !$rootScope.saveInProgress; continueUpdate = !$rootScope.saveInProgress;
} }
//Update form **if we are not currently updating** or if **shouldUpdateNow flag is set** //Update form **if we are not in the middle of an update** or if **shouldUpdateNow flag is set**
if(continueUpdate) { if(continueUpdate) {
var err = null; var err = null;
@ -119,11 +134,24 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$windo
$rootScope.saveInProgress = true; $rootScope.saveInProgress = true;
} }
if (isDiffed) { if (shouldDiff) {
//Do this so we can create duplicate fields
var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$');
for(var i=0; i < $scope.myform.form_fields.length; i++){
var field = $scope.myform.form_fields[i];
if(!checkForValidId.exec(field._id+'')){
delete $scope.myform.form_fields[i]._id;
delete $scope.myform.form_fields[i].id;
}
}
var data = DeepDiff.diff($scope.oldForm, $scope.myform);
$scope.updatePromise = $http.put('/forms/' + $scope.myform._id, {changes: data}) $scope.updatePromise = $http.put('/forms/' + $scope.myform._id, {changes: data})
.then(function (response) { .then(function (response) {
if (refreshAfterUpdate) { if (refreshAfterUpdate) {
$rootScope.myform = $scope.myform = response.data; $rootScope.myform = $scope.myform = response.data;
$scope.oldForm = _.cloneDeep($scope.myform);
} }
}).catch(function (response) { }).catch(function (response) {
err = response.data; err = response.data;
@ -146,6 +174,22 @@ angular.module('forms').controller('AdminFormController', ['$rootScope', '$windo
delete dataToSend.submissions; delete dataToSend.submissions;
} }
if(dataToSend.visible_form_fields){
delete dataToSend.visible_form_fields;
}
if(dataToSend.analytics){
delete dataToSend.analytics.visitors;
delete dataToSend.analytics.fields;
delete dataToSend.analytics.submissions;
delete dataToSend.analytics.views;
delete dataToSend.analytics.conversionRate;
}
delete dataToSend.created;
delete dataToSend.lastModified;
delete dataToSend.__v;
$scope.updatePromise = $http.put('/forms/' + $scope.myform._id, {form: dataToSend}) $scope.updatePromise = $http.put('/forms/' + $scope.myform._id, {form: dataToSend})
.then(function (response) { .then(function (response) {
if (refreshAfterUpdate) { if (refreshAfterUpdate) {

View file

@ -1,3 +1,4 @@
'use strict'; 'use strict';
angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormFields', '$uibModal', angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormFields', '$uibModal',
@ -23,7 +24,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
forceHelperSize: true, forceHelperSize: true,
forcePlaceholderSize: true, forcePlaceholderSize: true,
update: function(e, ui) { update: function(e, ui) {
$scope.update(false, $scope.myform, false, false, function(err){ $scope.update(false, $scope.myform, true, false, function(err){
}); });
}, },
}; };
@ -129,7 +130,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
$scope.saveField = function(){ $scope.saveField = function(){
$scope.myform.form_fields.push(curr_field); $scope.myform.form_fields.push(curr_field);
$scope.$parent.update(false, $scope.$parent.myform, false, true, function(){ $scope.$parent.update(false, $scope.$parent.myform, true, true, function(){
$uibModalInstance.close(); $uibModalInstance.close();
}); });
}; };
@ -184,7 +185,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
}; };
$scope.saveStartPage = function(){ $scope.saveStartPage = function(){
$scope.$parent.update(false, $scope.$parent.myform, false, true, function(){ $scope.$parent.update(false, $scope.$parent.myform, true, true, function(){
$uibModalInstance.close(); $uibModalInstance.close();
}); });
}; };
@ -196,7 +197,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
}; };
/* /*
** EditStartPageModal Functions ** EditEndPageModal Functions
*/ */
$scope.openEditEndPageModal = function(){ $scope.openEditEndPageModal = function(){
$scope.editEndPageModal = $uibModal.open({ $scope.editEndPageModal = $uibModal.open({
@ -239,7 +240,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
}; };
$scope.saveEndPage = function(){ $scope.saveEndPage = function(){
$scope.$parent.update(false, $scope.$parent.myform, false, true, function(){ $scope.$parent.update(false, $scope.$parent.myform, true, true, function(){
$uibModalInstance.close(); $uibModalInstance.close();
}); });
}; };

View file

@ -59,16 +59,16 @@
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<uib-tabset active="activePill" vertical="true" type="pills"> <uib-tabset active="activePill" vertical="true" type="pills">
<uib-tab index="0" heading="{{ 'CREATE_TAB' | translate }}"> <uib-tab index="0" heading="{{ 'CREATE_TAB' | translate }}" select="deactivateDesignTab()">
<edit-form-directive myform="myform"></edit-form-directive> <edit-form-directive myform="myform"></edit-form-directive>
</uib-tab> </uib-tab>
<uib-tab ng-repeat="tab in tabData" index="{{$index+1}}" heading="{{tab.heading}}"> <uib-tab ng-repeat="tab in tabData" index="{{$index+1}}" heading="{{tab.heading}}" select="deactivateDesignTab()">
<div class='row' data-ng-include="'/static/modules/forms/admin/views/adminTabs/'+tab.templateName+'.html'"></div> <div class='row' data-ng-include="'/static/modules/forms/admin/views/adminTabs/'+tab.templateName+'.html'"></div>
</uib-tab> </uib-tab>
<uib-tab index="2" heading="{{ 'ANALYZE_TAB' | translate }}"> <uib-tab index="2" heading="{{ 'ANALYZE_TAB' | translate }}" select="deactivateDesignTab()">
<edit-submissions-form-directive myform="myform" user="myform.admin"></edit-submissions-form-directive> <edit-submissions-form-directive myform="myform" user="myform.admin"></edit-submissions-form-directive>
</uib-tab> </uib-tab>
<uib-tab ng-if="tabData" heading="{{ 'SHARE_TAB' | translate }}" index="{{tabData.length}}"> <uib-tab ng-if="tabData" heading="{{ 'SHARE_TAB' | translate }}" index="{{tabData.length}}" select="deactivateDesignTab()">
<div class="config-form"> <div class="config-form">
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
@ -114,7 +114,8 @@
</div> </div>
</div> </div>
</uib-tab> </uib-tab>
<uib-tab ng-if="tabData && myform.form_fields.length" heading="{{ 'DESIGN_TAB' | translate }}" index="{{tabData.length}}+1"> <uib-tab class="design-tab" ng-if="tabData && myform.form_fields.length" heading="{{ 'DESIGN_TAB' | translate }}" index="{{tabData.length}}+1"
select="activateDesignTab()">
<div class="config-form design container"> <div class="config-form design container">
<div class="row"> <div class="row">
<div class="col-sm-4 col-xs-12"> <div class="col-sm-4 col-xs-12">
@ -175,7 +176,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm-8 hidden-xs"> <div class="col-sm-8 hidden-xs" ng-if="designTabActive">
<div class="public-form" ng-style="{ 'background-color': myform.design.colors.backgroundColor }"> <div class="public-form" ng-style="{ 'background-color': myform.design.colors.backgroundColor }">
<iframe id="iframe" ng-if="!!formURL" ng-src="{{formURL | trustSrc}}" style="border: none; box-shadow: 0px 0px 10px 0px grey; overflow: hidden; height: 400px; width: 90%; position: absolute;"></iframe> <iframe id="iframe" ng-if="!!formURL" ng-src="{{formURL | trustSrc}}" style="border: none; box-shadow: 0px 0px 10px 0px grey; overflow: hidden; height: 400px; width: 90%; position: absolute;"></iframe>
</div> </div>
@ -184,7 +185,7 @@
<div class="row"> <div class="row">
<div class="col-sm-offset-4 col-sm-2"> <div class="col-sm-offset-4 col-sm-2">
<button class="btn btn-signup btn-rounded" type="button" ng-click="update(false, myform, false, false, null)"><i class="icon-arrow-left icon-white"></i>{{ 'SAVE_CHANGES' | translate }}</button> <button class="btn btn-signup btn-rounded" type="button" ng-click="updateDesign(false, myform, false, false)"><i class="icon-arrow-left icon-white"></i>{{ 'SAVE_CHANGES' | translate }}</button>
</div> </div>
<div class="col-sm-1"> <div class="col-sm-1">
<button class="btn btn-secondary btn-rounded" type="button" ng-click="resetForm()"><i class="icon-eye-open icon-white"></i>{{ 'CANCEL' | translate }}</button> <button class="btn btn-secondary btn-rounded" type="button" ng-click="resetForm()"><i class="icon-eye-open icon-white"></i>{{ 'CANCEL' | translate }}</button>

View file

@ -1,3 +1,8 @@
form .btn {
border-color: grey;
}
.public-form.preview { .public-form.preview {
border: none; border: none;
box-shadow: 0px 0px 10px 0px grey; box-shadow: 0px 0px 10px 0px grey;
@ -11,37 +16,38 @@
.public-form input, .public-form textarea { .public-form input, .public-form textarea {
background-color: #000000; background-color: #000000;
background-color: rgba(0,0,0,0); background-color: rgba(0,0,0,0);
border: 2px dashed #ddd!important; border-width: 0px;
} }
.public-form input:focus, .public-form textarea:focus { form .btn {
border: 2px dashed #ddd!important; border-color: grey;
}
.public-form input.ng-untouched, .public-form textarea.ng-untouched {
border-width: 0 0 2px 0;
border-color: rgba(246, 255, 181, 0.4);
outline: 0; outline: 0;
} }
/*.public-form input.no-border.ng-invalid, .public-form textarea.no-border { .public-form input:focus, .public-form textarea:focus {
border-color: none; border-width: 0 0 2px 0;
}*/ border-color: rgba(246, 255, 181, 0.4);
.public-form input.ng-valid, .public-form textarea.ng-valid { outline: 0;
/*border-color: #20FF20!important;
border-style: solid!important;
border-width: 3px!important;*/
} }
.public-form input.ng-invalid.ng-dirty, .public-form textarea.ng-invalid.ng-dirty { .public-form input.ng-dirty, .public-form textarea.ng-dirty {
/*border-color: #FA787E!important; border-width: 0;
border-style: solid!important; }
border-width: 3px!important;*/
.public-form input.empty, .public-form textarea.empty {
border-width: 0 0 2px 0;
border-color: rgba(246, 255, 181, 0.4);
} }
section.content p.breakwords { section.content p.breakwords {
word-break: break-all; word-break: break-all;
} }
.public-form .btn {
border: 1px solid #c6c6c6;
}
.public-form .btn[type='submit'] { .public-form .btn[type='submit'] {
font-size: 1.5em; font-size: 1.5em;
padding: 0.35em 1.2em 0.35em 1.2em; padding: 0.35em 1.2em 0.35em 1.2em;
@ -142,7 +148,7 @@ form .row.field {
form .row.field > .field-title { form .row.field > .field-title {
margin-top:0.5em; margin-top:0.5em;
font-size:1.2em; font-size:1.2em;
padding-bottom: 1.8em; padding-bottom: 0.5em;
width: inherit; width: inherit;
} }
form .row.field > .field-input { form .row.field > .field-input {
@ -243,15 +249,12 @@ form .row.field {
} }
/* Styles for form list view (/forms) */ /* Styles for form list view (/forms) */
section.public-form { section.public-form field-directive .btn.btn-lg.btn-default {
padding: 0 10% 0 10%; background: none;
}
section.public-form .form-submitted {
height: 100vh;
} }
section.public-form .btn { section.public-form field-directive .btn[disabled]{
border: 1px solid; display: none;
} }
.form-item { .form-item {
@ -330,42 +333,16 @@ section.public-form .btn {
top:0; top:0;
} }
/*Modal overlay (for lightbox effect)*/ .field-directive {
.overlay { opacity: 0.2;
position: fixed; padding: 2.5% 10% 2.5% 10%;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: #000;
background-color: rgba(0,0,0,0.5);
z-index: 10;
}
.overlay.submitform {
background-color: #fff;
background-color: rgba(256,256,256,0.8);
} }
.field-directive { .form-field-wrapper .form-actions.activeField, .field-directive.activeField {
z-index: 9; opacity: 1;
padding: 10% 10% 10% 0;
border: 25px transparent solid;
position: relative;
} }
.activeField {
z-index: 11;
position: relative;
background-color: transparent;
}
.activeField.field-directive {
display: inline-block;
border-radius: 7px;
width: 100%;
border: 25px transparent solid;
}
.activeField input {
background-color: transparent;
}
h3.forms-list-title { h3.forms-list-title {
color: #3FA2F7; color: #3FA2F7;
font-weight: 600; font-weight: 600;

View file

@ -60,22 +60,13 @@
var newFakeModal = function(){ var newFakeModal = function(){
var result = { var result = {
opened: true, opened: true,
result: {
then: function(confirmCallback, cancelCallback) {
//Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
this.confirmCallBack = confirmCallback;
this.cancelCallback = cancelCallback;
}
},
close: function( item ) { close: function( item ) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item //The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.opened = false; this.opened = false;
this.result.confirmCallBack( item );
}, },
dismiss: function( type ) { dismiss: function( type ) {
//The user clicked cancel on the modal dialog, call the stored cancel callback //The user clicked cancel on the modal dialog, call the stored cancel callback
this.opened = false; this.opened = false;
this.result.cancelCallback( type );
} }
}; };
return result; return result;
@ -173,7 +164,6 @@
beforeEach(inject(function($uibModal) { beforeEach(inject(function($uibModal) {
var modal = newFakeModal(); var modal = newFakeModal();
spyOn($uibModal, 'open').and.returnValue(modal); spyOn($uibModal, 'open').and.returnValue(modal);
//spyOn($uibModal, 'close').and.callFake(modal.close());
})); }));
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_). // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).

View file

@ -115,7 +115,3 @@ input.form-control {
font-size: 18px; font-size: 18px;
padding: 20px 10px; padding: 20px 10px;
} }
.btn {
border: none;
}