merged
This commit is contained in:
commit
a8530472d5
|
@ -19,12 +19,12 @@ var mongoose = require('mongoose'),
|
||||||
*/
|
*/
|
||||||
exports.uploadPDF = function(req, res, next) {
|
exports.uploadPDF = function(req, res, next) {
|
||||||
|
|
||||||
console.log('inside uploadPDF');
|
//console.log('inside uploadPDF');
|
||||||
|
|
||||||
// console.log('\n\nProperty Descriptor\n-----------');
|
// console.log('\n\nProperty Descriptor\n-----------');
|
||||||
// console.log(Object.getOwnPropertyDescriptor(req.files.file, 'path'));
|
// console.log(Object.getOwnPropertyDescriptor(req.files.file, 'path'));
|
||||||
|
|
||||||
console.log(req.file);
|
//console.log(req.file);
|
||||||
|
|
||||||
if(req.file){
|
if(req.file){
|
||||||
var pdfFile = req.file;
|
var pdfFile = req.file;
|
||||||
|
@ -159,9 +159,20 @@ exports.deleteSubmissions = function(req, res) {
|
||||||
res.status(400).send({
|
res.status(400).send({
|
||||||
message: errorHandler.getErrorMessage(err)
|
message: errorHandler.getErrorMessage(err)
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).send('Form submissions successfully deleted');
|
form.analytics.visitors = [];
|
||||||
|
form.save(function(err){
|
||||||
|
if(err){
|
||||||
|
res.status(400).send({
|
||||||
|
message: errorHandler.getErrorMessage(err)
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.status(200).send('Form submissions successfully deleted');
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,8 +182,6 @@ exports.deleteSubmissions = function(req, res) {
|
||||||
exports.createSubmission = function(req, res) {
|
exports.createSubmission = function(req, res) {
|
||||||
|
|
||||||
var form = req.form;
|
var form = req.form;
|
||||||
// console.log('in createSubmission()');
|
|
||||||
// console.log(req.body);
|
|
||||||
|
|
||||||
var submission = new FormSubmission({
|
var submission = new FormSubmission({
|
||||||
admin: req.form.admin._id,
|
admin: req.form.admin._id,
|
||||||
|
@ -210,10 +219,9 @@ exports.createSubmission = function(req, res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
submission.save(function(err, submission){
|
submission.save(function(err, submission){
|
||||||
// console.log('in submissions.save()\n submission: '+JSON.stringify(submission) )
|
|
||||||
if(err){
|
if(err){
|
||||||
console.log(err.message);
|
console.log(err.message);
|
||||||
res.status(400).send({
|
res.status(500).send({
|
||||||
message: errorHandler.getErrorMessage(err)
|
message: errorHandler.getErrorMessage(err)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -281,8 +289,9 @@ exports.create = function(req, res) {
|
||||||
exports.read = function(req, res) {
|
exports.read = function(req, res) {
|
||||||
var validUpdateTypes= Form.schema.path('plugins.oscarhost.settings.updateType').enumValues;
|
var validUpdateTypes= Form.schema.path('plugins.oscarhost.settings.updateType').enumValues;
|
||||||
|
|
||||||
var newForm = JSON.parse(JSON.stringify(req.form));
|
var newForm = req.form.toJSON({virtuals : true});
|
||||||
newForm.plugins.oscarhost.settings.validUpdateTypes = validUpdateTypes;
|
newForm.plugins.oscarhost.settings.validUpdateTypes = validUpdateTypes;
|
||||||
|
|
||||||
res.json(newForm);
|
res.json(newForm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -377,11 +386,12 @@ exports.formByID = function(req, res, next, id) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//Remove sensitive information from User object
|
//Remove sensitive information from User object
|
||||||
form.admin.password = undefined;
|
var _form = form;
|
||||||
form.admin.salt = undefined;
|
_form.admin.password = undefined;
|
||||||
form.provider = undefined;
|
_form.admin.salt = undefined;
|
||||||
|
_form.provider = undefined;
|
||||||
|
|
||||||
req.form = form;
|
req.form = _form;
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,6 +46,21 @@ var ButtonSchema = new Schema({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var VisitorDataSchema = new Schema({
|
||||||
|
referrer: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
lastActiveField: {
|
||||||
|
type: Schema.Types.ObjectId
|
||||||
|
},
|
||||||
|
timeElapsed: {
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
isSubmitted: {
|
||||||
|
type: Boolean
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form Schema
|
* Form Schema
|
||||||
*/
|
*/
|
||||||
|
@ -65,10 +80,15 @@ var FormSchema = new Schema({
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
form_fields: {
|
|
||||||
type: [FieldSchema]
|
analytics:{
|
||||||
|
gaCode: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
visitors: [VisitorDataSchema]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
form_fields: [FieldSchema],
|
||||||
submissions: [{
|
submissions: [{
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'FormSubmission'
|
ref: 'FormSubmission'
|
||||||
|
@ -195,16 +215,91 @@ var FormSchema = new Schema({
|
||||||
},
|
},
|
||||||
auth: {
|
auth: {
|
||||||
user: {
|
user: {
|
||||||
type: String,
|
type: String
|
||||||
},
|
},
|
||||||
pass: {
|
pass: {
|
||||||
type: String,
|
type: String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
** In-Form Analytics Virtual Attributes
|
||||||
|
*/
|
||||||
|
FormSchema.virtual('analytics.views').get(function () {
|
||||||
|
return this.analytics.visitors.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
FormSchema.virtual('analytics.submissions').get(function () {
|
||||||
|
return this.submissions.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
FormSchema.virtual('analytics.conversionRate').get(function () {
|
||||||
|
return this.submissions.length/this.analytics.visitors.length*100;
|
||||||
|
});
|
||||||
|
|
||||||
|
FormSchema.virtual('analytics.fields').get(function () {
|
||||||
|
var fieldDropoffs = [];
|
||||||
|
var visitors = this.analytics.visitors;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
for(var i=0; i<this.form_fields.length; i++){
|
||||||
|
var field = this.form_fields[i];
|
||||||
|
|
||||||
|
if(!field.deletePreserved){
|
||||||
|
|
||||||
|
var dropoffViews = _.reduce(visitors, function(sum, visitorObj){
|
||||||
|
|
||||||
|
if(visitorObj.lastActiveField+'' === field._id+'' && !visitorObj.isSubmitted){
|
||||||
|
return sum + 1;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
var continueViews, nextIndex;
|
||||||
|
|
||||||
|
if(i !== this.form_fields.length-1){
|
||||||
|
continueViews = _.reduce(visitors, function(sum, visitorObj){
|
||||||
|
nextIndex = that.form_fields.indexOf(_.find(that.form_fields, function(o) {
|
||||||
|
return o._id+'' === visitorObj.lastActiveField+'';
|
||||||
|
}));
|
||||||
|
|
||||||
|
if(nextIndex > i){
|
||||||
|
return sum + 1;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}, 0);
|
||||||
|
}else {
|
||||||
|
continueViews = _.reduce(visitors, function(sum, visitorObj){
|
||||||
|
if(visitorObj.lastActiveField+'' === field._id+'' && visitorObj.isSubmitted){
|
||||||
|
return sum + 1;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalViews = dropoffViews+continueViews;
|
||||||
|
var continueRate = continueViews/totalViews*100;
|
||||||
|
var dropoffRate = dropoffViews/totalViews*100;
|
||||||
|
|
||||||
|
fieldDropoffs[i] = {
|
||||||
|
dropoffViews: dropoffViews,
|
||||||
|
continueViews: continueViews,
|
||||||
|
totalViews: totalViews,
|
||||||
|
continueRate: continueRate,
|
||||||
|
dropoffRate: dropoffRate,
|
||||||
|
field: field
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldDropoffs;
|
||||||
|
});
|
||||||
|
|
||||||
FormSchema.plugin(mUtilities.timestamp, {
|
FormSchema.plugin(mUtilities.timestamp, {
|
||||||
createdPath: 'created',
|
createdPath: 'created',
|
||||||
modifiedPath: 'lastModified',
|
modifiedPath: 'lastModified',
|
||||||
|
@ -397,7 +492,7 @@ FormSchema.pre('save', function (next) {
|
||||||
|
|
||||||
//Find FormSubmissions that contain field with _id equal to 'deleted_id'
|
//Find FormSubmissions that contain field with _id equal to 'deleted_id'
|
||||||
FormSubmission.
|
FormSubmission.
|
||||||
find({ form: that._id, admin: that.admin, form_fields: {$elemMatch: {_id: deleted_id} } }).
|
find({ form: that._id, admin: that.admin, form_fields: {$elemMatch: {submissionId: deleted_id} } }).
|
||||||
exec(function(err, submissions){
|
exec(function(err, submissions){
|
||||||
if(err) {
|
if(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Module dependencies.
|
* Module dependencies.
|
||||||
*/
|
*/
|
||||||
var mongoose = require('mongoose'),
|
var mongoose = require('mongoose'),
|
||||||
|
util = require('util'),
|
||||||
mUtilities = require('mongoose-utilities'),
|
mUtilities = require('mongoose-utilities'),
|
||||||
_ = require('lodash'),
|
_ = require('lodash'),
|
||||||
Schema = mongoose.Schema;
|
Schema = mongoose.Schema;
|
||||||
|
@ -23,85 +24,188 @@ var FieldOptionSchema = new Schema({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var RatingFieldSchema = new Schema({
|
||||||
|
steps: {
|
||||||
|
type: Number,
|
||||||
|
min: 1,
|
||||||
|
max: 10
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
type: String,
|
||||||
|
enum: [
|
||||||
|
'Heart',
|
||||||
|
'Star',
|
||||||
|
'thumbs-up',
|
||||||
|
'thumbs-down',
|
||||||
|
'Circle',
|
||||||
|
'Square',
|
||||||
|
'Check Circle',
|
||||||
|
'Smile Outlined',
|
||||||
|
'Hourglass',
|
||||||
|
'bell',
|
||||||
|
'Paper Plane',
|
||||||
|
'Comment',
|
||||||
|
'Trash'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
validShapes: {
|
||||||
|
type: [String]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FormField Schema
|
* FormField Schema
|
||||||
*/
|
*/
|
||||||
var FormFieldSchema = new Schema({
|
function BaseFieldSchema(){
|
||||||
title: {
|
Schema.apply(this, arguments);
|
||||||
type: String,
|
|
||||||
trim: true,
|
|
||||||
required: 'Field Title cannot be blank'
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
|
|
||||||
logicJump: {
|
this.add({
|
||||||
type: Schema.Types.ObjectId,
|
isSubmission: {
|
||||||
ref: 'LogicJump'
|
type: Boolean,
|
||||||
},
|
default: false
|
||||||
|
},
|
||||||
|
submissionId: {
|
||||||
|
type: Schema.Types.ObjectId
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
required: 'Field Title cannot be blank'
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
|
||||||
fieldOptions: [FieldOptionSchema],
|
logicJump: {
|
||||||
required: {
|
type: Schema.Types.ObjectId,
|
||||||
type: Boolean,
|
ref: 'LogicJump'
|
||||||
default: true
|
},
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
|
|
||||||
deletePreserved: {
|
ratingOptions: {
|
||||||
type: Boolean,
|
type: RatingFieldSchema,
|
||||||
default: false
|
required: false,
|
||||||
},
|
default: {}
|
||||||
validFieldTypes: {
|
},
|
||||||
type: [String]
|
fieldOptions: [FieldOptionSchema],
|
||||||
},
|
required: {
|
||||||
fieldType: {
|
type: Boolean,
|
||||||
type: String,
|
default: true
|
||||||
required: true,
|
},
|
||||||
enum: [
|
disabled: {
|
||||||
'textfield',
|
type: Boolean,
|
||||||
'date',
|
default: false
|
||||||
'email',
|
},
|
||||||
'link',
|
|
||||||
'legal',
|
deletePreserved: {
|
||||||
'url',
|
type: Boolean,
|
||||||
'textarea',
|
default: false
|
||||||
'statement',
|
},
|
||||||
'welcome',
|
validFieldTypes: {
|
||||||
'thankyou',
|
type: [String]
|
||||||
'file',
|
},
|
||||||
'dropdown',
|
fieldType: {
|
||||||
'scale',
|
type: String,
|
||||||
'rating',
|
required: true,
|
||||||
'radio',
|
enum: [
|
||||||
'checkbox',
|
'textfield',
|
||||||
'hidden',
|
'date',
|
||||||
'yes_no',
|
'email',
|
||||||
'natural',
|
'link',
|
||||||
'number'
|
'legal',
|
||||||
]
|
'url',
|
||||||
},
|
'textarea',
|
||||||
fieldValue: Schema.Types.Mixed
|
'statement',
|
||||||
|
'welcome',
|
||||||
|
'thankyou',
|
||||||
|
'file',
|
||||||
|
'dropdown',
|
||||||
|
'scale',
|
||||||
|
'rating',
|
||||||
|
'radio',
|
||||||
|
'checkbox',
|
||||||
|
'hidden',
|
||||||
|
'yes_no',
|
||||||
|
'natural',
|
||||||
|
'number'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
fieldValue: Schema.Types.Mixed
|
||||||
|
});
|
||||||
|
|
||||||
|
this.plugin(mUtilities.timestamp, {
|
||||||
|
createdPath: 'created',
|
||||||
|
modifiedPath: 'lastModified',
|
||||||
|
useVirtual: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.pre('save', function (next) {
|
||||||
|
this.validFieldTypes = mongoose.model('Field').schema.path('fieldType').enumValues;
|
||||||
|
|
||||||
|
if(this.fieldType === 'rating' && this.ratingOptions.validShapes.length === 0){
|
||||||
|
this.ratingOptions.validShapes = mongoose.model('RatingOptions').schema.path('shape').enumValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(BaseFieldSchema, Schema);
|
||||||
|
|
||||||
|
var FormFieldSchema = new BaseFieldSchema();
|
||||||
|
|
||||||
|
FormFieldSchema.pre('validate', function(next) {
|
||||||
|
var error = new mongoose.Error.ValidationError(this);
|
||||||
|
|
||||||
|
//If field is rating check that it has ratingOptions
|
||||||
|
if(this.fieldType !== 'rating'){
|
||||||
|
|
||||||
|
if(this.ratingOptions && this.ratingOptions.steps && this.ratingOptions.shape){
|
||||||
|
error.errors.ratingOptions = new mongoose.Error.ValidatorError({path: 'ratingOptions', message: 'ratingOptions is only allowed for type \'rating\' fields.', type: 'notvalid', value: this.ratingOptions});
|
||||||
|
return(next(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
//Setting default values for ratingOptions
|
||||||
|
if(!this.ratingOptions.steps){
|
||||||
|
this.ratingOptions.steps = 10;
|
||||||
|
}
|
||||||
|
if(!this.ratingOptions.shape){
|
||||||
|
this.ratingOptions.shape = 'Star';
|
||||||
|
}
|
||||||
|
|
||||||
|
//Checking that the fieldValue is between 0 and ratingOptions.steps
|
||||||
|
if(this.fieldValue+0 > this.ratingOptions.steps || this.fieldValue+0 < 0){
|
||||||
|
this.fieldValue = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//If field is multiple choice check that it has field
|
||||||
|
if(this.fieldType !== 'dropdown' && this.fieldType !== 'radio' && this.fieldType !== 'checkbox'){
|
||||||
|
if(!this.fieldOptions || this.fieldOptions.length !== 0){
|
||||||
|
error.errors.ratingOptions = new mongoose.Error.ValidatorError({path:'fieldOptions', message: 'fieldOptions are only allowed for type dropdown, checkbox or radio fields.', type: 'notvalid', value: this.ratingOptions});
|
||||||
|
return(next(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
FormFieldSchema.plugin(mUtilities.timestamp, {
|
//Submission fieldValue correction
|
||||||
createdPath: 'created',
|
FormFieldSchema.pre('save', function(next) {
|
||||||
modifiedPath: 'lastModified',
|
|
||||||
useVirtual: false
|
|
||||||
});
|
|
||||||
|
|
||||||
FormFieldSchema.pre('save', function (next){
|
if(this.fieldType === 'dropdown' && this.isSubmission){
|
||||||
this.validFieldTypes = mongoose.model('Field').schema.path('fieldType').enumValues;
|
//console.log(this);
|
||||||
next();
|
this.fieldValue = this.fieldValue.option_value;
|
||||||
|
//console.log(this.fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
mongoose.model('Field', FormFieldSchema);
|
var Field = mongoose.model('Field', FormFieldSchema);
|
||||||
|
var RatingOptions = mongoose.model('RatingOptions', RatingFieldSchema);
|
||||||
|
|
||||||
module.exports = FormFieldSchema;
|
module.exports = FormFieldSchema;
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ var mongoose = require('mongoose'),
|
||||||
FieldSchema = require('./form_field.server.model.js'),
|
FieldSchema = require('./form_field.server.model.js'),
|
||||||
OscarSecurity = require('../../scripts/oscarhost/OscarSecurity');
|
OscarSecurity = require('../../scripts/oscarhost/OscarSecurity');
|
||||||
|
|
||||||
|
var FieldSchema = require('./form_field.server.model.js');
|
||||||
|
|
||||||
var newDemoTemplate = {
|
var newDemoTemplate = {
|
||||||
address: '880-9650 Velit. St.',
|
address: '880-9650 Velit. St.',
|
||||||
city: '',
|
city: '',
|
||||||
|
@ -41,6 +43,18 @@ var newDemoTemplate = {
|
||||||
yearOfBirth: '2015'
|
yearOfBirth: '2015'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Setter function for form_fields
|
||||||
|
function formFieldsSetter(form_fields){
|
||||||
|
for(var i=0; i<form_fields.length; i++){
|
||||||
|
form_fields[i].isSubmission = true;
|
||||||
|
form_fields[i].submissionId = form_fields[i]._id;
|
||||||
|
form_fields[i]._id = new mongoose.mongo.ObjectID();
|
||||||
|
}
|
||||||
|
//console.log(form_fields)
|
||||||
|
return form_fields;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form Submission Schema
|
* Form Submission Schema
|
||||||
*/
|
*/
|
||||||
|
@ -55,9 +69,7 @@ var FormSubmissionSchema = new Schema({
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
|
||||||
form_fields: {
|
form_fields: [FieldSchema],
|
||||||
type: [Schema.Types.Mixed]
|
|
||||||
},
|
|
||||||
|
|
||||||
form: {
|
form: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
|
@ -99,7 +111,7 @@ var FormSubmissionSchema = new Schema({
|
||||||
},
|
},
|
||||||
|
|
||||||
timeElapsed: {
|
timeElapsed: {
|
||||||
type: Number,
|
type: Number
|
||||||
},
|
},
|
||||||
percentageComplete: {
|
percentageComplete: {
|
||||||
type: Number
|
type: Number
|
||||||
|
@ -116,9 +128,10 @@ var FormSubmissionSchema = new Schema({
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
FormSubmissionSchema.path('form_fields').set(formFieldsSetter);
|
||||||
|
|
||||||
FormSubmissionSchema.plugin(mUtilities.timestamp, {
|
FormSubmissionSchema.plugin(mUtilities.timestamp, {
|
||||||
createdPath: 'created',
|
createdPath: 'created',
|
||||||
modifiedPath: 'lastModified',
|
modifiedPath: 'lastModified',
|
||||||
|
@ -214,7 +227,7 @@ FormSubmissionSchema.pre('save', function (next) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
], function(err, result) {
|
], function(err, result) {
|
||||||
if(err) return next(err);
|
if(err) return next(err);
|
||||||
|
@ -254,7 +267,6 @@ FormSubmissionSchema.pre('save', function (next) {
|
||||||
self = this,
|
self = this,
|
||||||
_form = this.form;
|
_form = this.form;
|
||||||
|
|
||||||
|
|
||||||
if(this.pdf && this.pdf.path){
|
if(this.pdf && this.pdf.path){
|
||||||
dest_filename = self.title.replace(/ /g,'')+'_submission_'+Date.now()+'.pdf';
|
dest_filename = self.title.replace(/ /g,'')+'_submission_'+Date.now()+'.pdf';
|
||||||
var __path = this.pdf.path.split('/').slice(0,this.pdf.path.split('/').length-1).join('/');
|
var __path = this.pdf.path.split('/').slice(0,this.pdf.path.split('/').length-1).join('/');
|
||||||
|
|
80
app/sockets/analytics_service.js
Normal file
80
app/sockets/analytics_service.js
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
var mongoose = require('mongoose'),
|
||||||
|
config = require('../../config/config'),
|
||||||
|
errorHandler = require('../controllers/errors.server.controller'),
|
||||||
|
Form = mongoose.model('Form');
|
||||||
|
|
||||||
|
// Create the chat configuration
|
||||||
|
module.exports = function (io, socket) {
|
||||||
|
var visitorsData = {};
|
||||||
|
|
||||||
|
var saveVisitorData = function (data, cb){
|
||||||
|
|
||||||
|
Form.findById(data.formId, function(err, form) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw new Error(errorHandler.getErrorMessage(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
var newVisitor = {
|
||||||
|
referrer: data.referrer,
|
||||||
|
lastActiveField: data.lastActiveField,
|
||||||
|
timeElapsed: data.timeElapsed,
|
||||||
|
isSubmitted: data.isSubmitted
|
||||||
|
};
|
||||||
|
|
||||||
|
form.analytics.visitors.push(newVisitor);
|
||||||
|
|
||||||
|
form.save(function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw new Error(errorHandler.getErrorMessage(err));
|
||||||
|
}
|
||||||
|
console.log('\n\nVisitor data successfully added!');
|
||||||
|
|
||||||
|
delete visitorsData[socket.id];
|
||||||
|
|
||||||
|
if(cb) cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.disconnect(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
io.on('connection', function(socket) {
|
||||||
|
|
||||||
|
|
||||||
|
// a user has visited our page - add them to the visitorsData object
|
||||||
|
socket.on('form-visitor-data', function(data) {
|
||||||
|
|
||||||
|
console.log('\n\nuser has visited our page');
|
||||||
|
|
||||||
|
visitorsData[socket.id] = data;
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
if (data.isSubmitted) {
|
||||||
|
saveVisitorData(data, function () {
|
||||||
|
console.log('\n\n user submitted form');
|
||||||
|
|
||||||
|
socket.disconnect(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', function() {
|
||||||
|
var data = visitorsData[socket.id];
|
||||||
|
|
||||||
|
if(data){
|
||||||
|
if(!data.isSubmitted) {
|
||||||
|
saveVisitorData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
|
@ -80,8 +80,12 @@
|
||||||
<!--Embedding The signupDisabled Boolean-->
|
<!--Embedding The signupDisabled Boolean-->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var signupDisabled = {{signupDisabled | safe}};
|
var signupDisabled = {{signupDisabled | safe}};
|
||||||
|
var socketPort = {{socketPort | safe}};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!--Socket.io Client Dependency-->
|
||||||
|
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
|
||||||
|
|
||||||
<!--Bower JS dependencies-->
|
<!--Bower JS dependencies-->
|
||||||
{% for bowerJSFile in bowerJSFiles %}
|
{% for bowerJSFile in bowerJSFiles %}
|
||||||
<script type="text/javascript" src="{{bowerJSFile}}"></script>
|
<script type="text/javascript" src="{{bowerJSFile}}"></script>
|
||||||
|
@ -103,7 +107,7 @@
|
||||||
<script src="https://cdn.ravenjs.com/2.3.0/angular/raven.min.js"></script>
|
<script src="https://cdn.ravenjs.com/2.3.0/angular/raven.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
Raven.config('http://825fefd6b4ed4a4da199c1b832ca845c@sentry.tellform.com/2').install();
|
Raven.config('https://825fefd6b4ed4a4da199c1b832ca845c@sentry.tellform.com/2').install();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- [if lt IE 9]>
|
<!-- [if lt IE 9]>
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
"angular-bootstrap-colorpicker": "~3.0.19",
|
"angular-bootstrap-colorpicker": "~3.0.19",
|
||||||
"angular-ui-router-tabs": "~1.7.0",
|
"angular-ui-router-tabs": "~1.7.0",
|
||||||
"angular-scroll": "^1.0.0",
|
"angular-scroll": "^1.0.0",
|
||||||
"ui-select": "angular-ui-select#^0.16.1",
|
|
||||||
"angular-sanitize": "^1.5.3",
|
"angular-sanitize": "^1.5.3",
|
||||||
"v-button": "^1.1.1",
|
"v-button": "^1.1.1",
|
||||||
"angular-busy": "^4.1.3",
|
"angular-busy": "^4.1.3",
|
||||||
|
@ -35,10 +34,13 @@
|
||||||
"raven-js": "^3.0.4",
|
"raven-js": "^3.0.4",
|
||||||
"tableExport.jquery.plugin": "^1.5.1",
|
"tableExport.jquery.plugin": "^1.5.1",
|
||||||
"angular-translate": "~2.11.0"
|
"angular-translate": "~2.11.0"
|
||||||
|
"js-yaml": "^3.6.1",
|
||||||
|
"angular-ui-select": "whitef0x0/ui-select#compiled"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"angular-bootstrap": "^0.14.0",
|
"angular-bootstrap": "^0.14.0",
|
||||||
"angular": "1.4.x"
|
"angular": "~1.4.7",
|
||||||
|
"angular-ui-select": "compiled"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"BOWER-PACKAGE": {
|
"BOWER-PACKAGE": {
|
||||||
|
|
|
@ -12,6 +12,7 @@ var _ = require('lodash'),
|
||||||
var exists = require('path-exists').sync;
|
var exists = require('path-exists').sync;
|
||||||
|
|
||||||
var minBowerFiles = function(type){
|
var minBowerFiles = function(type){
|
||||||
|
console.log(type);
|
||||||
return bowerFiles(type).map( function(path, index, arr) {
|
return bowerFiles(type).map( function(path, index, arr) {
|
||||||
var newPath = path.replace(/.([^.]+)$/g, '.min.$1');
|
var newPath = path.replace(/.([^.]+)$/g, '.min.$1');
|
||||||
return exists( newPath ) ? newPath : path;
|
return exists( newPath ) ? newPath : path;
|
||||||
|
|
36
config/env/all.js
vendored
36
config/env/all.js
vendored
|
@ -7,7 +7,9 @@ module.exports = {
|
||||||
description: process.env.APP_DESC || 'Opensource form builder alternative to TypeForm',
|
description: process.env.APP_DESC || 'Opensource form builder alternative to TypeForm',
|
||||||
keywords: process.env.APP_KEYWORDS || 'typeform, pdfs, forms, opensource, formbuilder, google forms, nodejs'
|
keywords: process.env.APP_KEYWORDS || 'typeform, pdfs, forms, opensource, formbuilder, google forms, nodejs'
|
||||||
},
|
},
|
||||||
port: process.env.PORT || 3000,
|
port: process.env.PORT || 5000,
|
||||||
|
socketPort: process.env.SOCKET_PORT || 35729,
|
||||||
|
|
||||||
templateEngine: 'swig',
|
templateEngine: 'swig',
|
||||||
|
|
||||||
reCAPTCHA_Key: process.env.reCAPTCHA_KEY || '',
|
reCAPTCHA_Key: process.env.reCAPTCHA_KEY || '',
|
||||||
|
@ -70,9 +72,9 @@ module.exports = {
|
||||||
assets: {
|
assets: {
|
||||||
css: [
|
css: [
|
||||||
'public/modules/**/css/*.css',
|
'public/modules/**/css/*.css',
|
||||||
'!public/modules/**/demo/**/*.css',
|
'public/modules/**/demo/**/*.css',
|
||||||
'!public/modules/**/dist/**/*.css',
|
'public/modules/**/dist/**/*.css',
|
||||||
'!public/modules/**/node_modules/**/*.css'
|
'public/modules/**/node_modules/**/*.css'
|
||||||
],
|
],
|
||||||
js: [
|
js: [
|
||||||
'public/dist/populate_template_cache.js',
|
'public/dist/populate_template_cache.js',
|
||||||
|
@ -82,29 +84,29 @@ module.exports = {
|
||||||
'public/modules/*/*.js',
|
'public/modules/*/*.js',
|
||||||
'public/modules/*/*/*.js',
|
'public/modules/*/*/*.js',
|
||||||
'public/modules/**/*.js',
|
'public/modules/**/*.js',
|
||||||
'!public/modules/**/gruntfile.js',
|
'public/modules/**/gruntfile.js',
|
||||||
'!public/modules/**/demo/**/*.js',
|
'public/modules/**/demo/**/*.js',
|
||||||
'!public/modules/**/dist/**/*.js',
|
'public/modules/**/dist/**/*.js',
|
||||||
'!public/modules/**/node_modules/**/*.js',
|
'public/modules/**/node_modules/**/*.js',
|
||||||
'!public/modules/**/tests/**/*.js'
|
'public/modules/**/tests/**/*.js'
|
||||||
],
|
],
|
||||||
views: [
|
views: [
|
||||||
'public/modules/**/*.html',
|
'public/modules/**/*.html',
|
||||||
'!public/modules/**/demo/**/*.html',
|
'public/modules/**/demo/**/*.html',
|
||||||
'!public/modules/**/dist/**/*.html',
|
'public/modules/**/dist/**/*.html',
|
||||||
'!public/modules/**/node_modules/**/*.html',
|
'public/modules/**/node_modules/**/*.html',
|
||||||
'!public/modules/**/tests/**/*.html'
|
'public/modules/**/tests/**/*.html'
|
||||||
],
|
],
|
||||||
unit_tests: [
|
unit_tests: [
|
||||||
'public/lib/angular-mocks/angular-mocks.js',
|
'public/lib/angular-mocks/angular-mocks.js',
|
||||||
'public/modules/*/tests/unit/**/*.js',
|
'public/modules/*/tests/unit/**/*.js',
|
||||||
'!public/modules/**/demo/**/*.js',
|
'public/modules/**/demo/**/*.js',
|
||||||
'!public/modules/**/node_modules/**/*.js'
|
'public/modules/**/node_modules/**/*.js'
|
||||||
],
|
],
|
||||||
e2e_tests: [
|
e2e_tests: [
|
||||||
'public/modules/*/tests/e2e/**.js',
|
'public/modules/*/tests/e2e/**.js',
|
||||||
'!public/modules/**/demo/**/*.js',
|
'public/modules/**/demo/**/*.js',
|
||||||
'!public/modules/**/node_modules/**/*.js'
|
'public/modules/**/node_modules/**/*.js'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
2
config/env/development.js
vendored
2
config/env/development.js
vendored
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
baseUrl: process.env.BASE_URL || 'http://localhost:3000',
|
baseUrl: process.env.BASE_URL || 'http://localhost:5000',
|
||||||
db: {
|
db: {
|
||||||
uri: 'mongodb://'+(process.env.DB_HOST || 'localhost')+'/mean',
|
uri: 'mongodb://'+(process.env.DB_HOST || 'localhost')+'/mean',
|
||||||
options: {
|
options: {
|
||||||
|
|
17
config/env/local.js
vendored
17
config/env/local.js
vendored
|
@ -1,17 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var mandrill_api_key = 'AVCCf1C2dFlrNhx9Iyi_yQ';
|
|
||||||
|
|
||||||
//Use mandrill mock API_key if we are testing
|
|
||||||
// if(process.env.NODE_ENV === 'test') mandrill_api_key = '_YNOKLgT9DGb2sgVGR66yQ';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
// db: {
|
|
||||||
// uri: 'mongodb://localhost/local-dev',
|
|
||||||
// options: {
|
|
||||||
// user: '',
|
|
||||||
// pass: ''
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
sessionSecret: process.env.SESSION_SECRET || 'somethingheresecret',
|
|
||||||
};
|
|
65
config/env/production-dev.js
vendored
65
config/env/production-dev.js
vendored
|
@ -1,65 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
baseUrl: process.env.BASE_URL || 'dev.tellform.com',
|
|
||||||
db: {
|
|
||||||
uri: process.env.MONGOHQ_URL || process.env.MONGOLAB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean',
|
|
||||||
options: {
|
|
||||||
user: 'admin',
|
|
||||||
pass: process.env.MONGOLAB_PASS || 'admin'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
log: {
|
|
||||||
// Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
|
|
||||||
format: 'dev',
|
|
||||||
// Stream defaults to process.stdout
|
|
||||||
// Uncomment to enable logging to a log on the file system
|
|
||||||
options: {
|
|
||||||
stream: 'access.log'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sessionCookie: {
|
|
||||||
domain: process.env.BASE_URL || 'dev.tellform.com'
|
|
||||||
},
|
|
||||||
assets: {
|
|
||||||
css: 'public/dist/application.min.css',
|
|
||||||
js: 'public/dist/application.min.js'
|
|
||||||
},
|
|
||||||
facebook: {
|
|
||||||
clientID: process.env.FACEBOOK_ID || 'APP_ID',
|
|
||||||
clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
|
|
||||||
callbackURL: '/auth/facebook/callback'
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
|
|
||||||
clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
|
|
||||||
callbackURL: '/auth/twitter/callback'
|
|
||||||
},
|
|
||||||
google: {
|
|
||||||
clientID: process.env.GOOGLE_ID || 'APP_ID',
|
|
||||||
clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
|
|
||||||
callbackURL: '/auth/google/callback'
|
|
||||||
},
|
|
||||||
linkedin: {
|
|
||||||
clientID: process.env.LINKEDIN_ID || 'APP_ID',
|
|
||||||
clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
|
|
||||||
callbackURL: '/auth/linkedin/callback'
|
|
||||||
},
|
|
||||||
github: {
|
|
||||||
clientID: process.env.GITHUB_ID || 'APP_ID',
|
|
||||||
clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
|
|
||||||
callbackURL: '/auth/github/callback'
|
|
||||||
},
|
|
||||||
mailer: {
|
|
||||||
from: process.env.MAILER_FROM || 'no-reply@dev.tellform.com',
|
|
||||||
options: {
|
|
||||||
service: process.env.MAILER_SERVICE_PROVIDER || '',
|
|
||||||
secure: false,
|
|
||||||
requireTLS: true,
|
|
||||||
auth: {
|
|
||||||
user: process.env.MAILER_EMAIL_ID || '',
|
|
||||||
pass: process.env.MAILER_PASSWORD || ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -26,7 +26,16 @@ var fs = require('fs-extra'),
|
||||||
device = require('express-device'),
|
device = require('express-device'),
|
||||||
client = new raven.Client(config.DSN);
|
client = new raven.Client(config.DSN);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure Socket.io
|
||||||
|
*/
|
||||||
|
var configureSocketIO = function (app, db) {
|
||||||
|
// Load the Socket.io configuration
|
||||||
|
var server = require('./socket.io')(app, db);
|
||||||
|
|
||||||
|
// Return server object
|
||||||
|
return server;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = function(db) {
|
module.exports = function(db) {
|
||||||
// Initialize express app
|
// Initialize express app
|
||||||
|
@ -43,6 +52,7 @@ module.exports = function(db) {
|
||||||
app.locals.signupDisabled = config.signupDisabled;
|
app.locals.signupDisabled = config.signupDisabled;
|
||||||
app.locals.description = config.app.description;
|
app.locals.description = config.app.description;
|
||||||
app.locals.keywords = config.app.keywords;
|
app.locals.keywords = config.app.keywords;
|
||||||
|
app.locals.socketPort = config.socketPort;
|
||||||
|
|
||||||
app.locals.bowerJSFiles = config.getBowerJSAssets();
|
app.locals.bowerJSFiles = config.getBowerJSAssets();
|
||||||
app.locals.bowerCssFiles = config.getBowerCSSAssets();
|
app.locals.bowerCssFiles = config.getBowerCSSAssets();
|
||||||
|
@ -147,11 +157,11 @@ module.exports = function(db) {
|
||||||
|
|
||||||
|
|
||||||
// Add headers for Sentry
|
// Add headers for Sentry
|
||||||
/*
|
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
|
|
||||||
// Website you wish to allow to connect
|
// Website you wish to allow to connect
|
||||||
res.setHeader('Access-Control-Allow-Origin', 'http://sentry.polydaic.com');
|
res.setHeader('Access-Control-Allow-Origin', 'https://sentry.polydaic.com');
|
||||||
|
|
||||||
// Request methods you wish to allow
|
// Request methods you wish to allow
|
||||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
|
||||||
|
@ -166,7 +176,7 @@ module.exports = function(db) {
|
||||||
// Pass to next layer of middleware
|
// Pass to next layer of middleware
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
// Sentry (Raven) middleware
|
// Sentry (Raven) middleware
|
||||||
// app.use(raven.middleware.express.requestHandler(config.DSN));
|
// app.use(raven.middleware.express.requestHandler(config.DSN));
|
||||||
|
|
||||||
|
@ -212,6 +222,8 @@ module.exports = function(db) {
|
||||||
return httpsServer;
|
return httpsServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app = configureSocketIO(app, db);
|
||||||
|
|
||||||
// Return Express server instance
|
// Return Express server instance
|
||||||
return app;
|
return app;
|
||||||
};
|
};
|
||||||
|
|
24
config/socket.io.js
Normal file
24
config/socket.io.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Load the module dependencies
|
||||||
|
var config = require('./config'),
|
||||||
|
path = require('path'),
|
||||||
|
http = require('http'),
|
||||||
|
socketio = require('socket.io');
|
||||||
|
|
||||||
|
// Define the Socket.io configuration method
|
||||||
|
module.exports = function (app, db) {
|
||||||
|
var server = http.createServer(app);
|
||||||
|
|
||||||
|
// Create a new Socket.io server
|
||||||
|
var io = socketio.listen(server);
|
||||||
|
|
||||||
|
// Add an event listener to the 'connection' event
|
||||||
|
io.on('connection', function (socket) {
|
||||||
|
config.getGlobbedFiles('./app/sockets/**.js').forEach(function (socketConfiguration) {
|
||||||
|
require(path.resolve(socketConfiguration))(io, socket);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return server;
|
||||||
|
};
|
12
config/strategies/anonymous.js
Normal file
12
config/strategies/anonymous.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
var passport = require('passport'),
|
||||||
|
AnonymousStrategy = require('passport-anonymous').Strategy;
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
// Use local strategy
|
||||||
|
passport.use(new AnonymousStrategy());
|
||||||
|
};
|
|
@ -19,7 +19,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "grunt",
|
"start": "grunt",
|
||||||
"test": "grunt test && grunt coveralls",
|
"test": "grunt test && grunt coveralls",
|
||||||
"postinstall": "bower install --config.interactive=false; grunt build"
|
"postinstall": "bower install --config.interactive=false; grunt build; node scripts/setup.js;"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^1.4.2",
|
"async": "^1.4.2",
|
||||||
|
@ -63,13 +63,14 @@
|
||||||
"math": "0.0.3",
|
"math": "0.0.3",
|
||||||
"method-override": "~2.3.0",
|
"method-override": "~2.3.0",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"mongoose": "3.8.40",
|
"mongoose": "~4.4.19",
|
||||||
"mongoose-utilities": "~0.1.1",
|
"mongoose-utilities": "~0.1.1",
|
||||||
"morgan": "~1.6.1",
|
"morgan": "~1.6.1",
|
||||||
"multer": "~1.1.0",
|
"multer": "~1.1.0",
|
||||||
"node-freegeoip": "0.0.1",
|
"node-freegeoip": "0.0.1",
|
||||||
"nodemailer": "~1.10.0",
|
"nodemailer": "~1.10.0",
|
||||||
"passport": "~0.3.0",
|
"passport": "~0.3.0",
|
||||||
|
"passport-anonymous": "^1.0.1",
|
||||||
"passport-facebook": "~2.0.0",
|
"passport-facebook": "~2.0.0",
|
||||||
"passport-github": "~1.0.0",
|
"passport-github": "~1.0.0",
|
||||||
"passport-google-oauth": "~0.2.0",
|
"passport-google-oauth": "~0.2.0",
|
||||||
|
@ -82,6 +83,7 @@
|
||||||
"random-js": "^1.0.8",
|
"random-js": "^1.0.8",
|
||||||
"raven": "^0.9.0",
|
"raven": "^0.9.0",
|
||||||
"soap": "^0.11.0",
|
"soap": "^0.11.0",
|
||||||
|
"socket.io": "^1.4.6",
|
||||||
"swig": "~1.4.1"
|
"swig": "~1.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
526
public/dist/application.js
vendored
526
public/dist/application.js
vendored
File diff suppressed because one or more lines are too long
2
public/dist/application.min.css
vendored
2
public/dist/application.min.css
vendored
File diff suppressed because one or more lines are too long
12
public/dist/application.min.js
vendored
12
public/dist/application.min.js
vendored
File diff suppressed because one or more lines are too long
55
public/modules/core/services/socket.io.client.service.js
Normal file
55
public/modules/core/services/socket.io.client.service.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Create the Socket.io wrapper service
|
||||||
|
angular
|
||||||
|
.module('core')
|
||||||
|
.factory('Socket', Socket);
|
||||||
|
|
||||||
|
Socket.$inject = ['$timeout', '$window'];
|
||||||
|
|
||||||
|
function Socket($timeout, $window) {
|
||||||
|
var service = {
|
||||||
|
connect: connect,
|
||||||
|
emit: emit,
|
||||||
|
on: on,
|
||||||
|
removeListener: removeListener,
|
||||||
|
socket: null
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('https://'+window.location.hostname+':'+$window.socketPort);
|
||||||
|
connect('https://'+window.location.hostname+':'+$window.socketPort);
|
||||||
|
|
||||||
|
return service;
|
||||||
|
|
||||||
|
// Connect to Socket.io server
|
||||||
|
function connect(url) {
|
||||||
|
service.socket = io();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}());
|
|
@ -79,7 +79,7 @@ angular.module('forms').directive('autoSaveForm', ['$rootScope', '$timeout', fun
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Autosave Form when model (specificed in $attrs.autoSaveWatch) changes
|
//Autosave Form when model (specified in $attrs.autoSaveWatch) changes
|
||||||
$scope.$watch($attrs.autoSaveWatch, function(newValue, oldValue) {
|
$scope.$watch($attrs.autoSaveWatch, function(newValue, oldValue) {
|
||||||
|
|
||||||
newValue = angular.copy(newValue);
|
newValue = angular.copy(newValue);
|
||||||
|
|
|
@ -24,8 +24,25 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
||||||
forcePlaceholderSize: true
|
forcePlaceholderSize: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
console.log($scope.sortableOptions);
|
** Setup Angular-Input-Star Shape Dropdown
|
||||||
|
*/
|
||||||
|
//Populate Name to Font-awesomeName Conversion Map
|
||||||
|
$scope.select2FA = {
|
||||||
|
'Heart': 'Heart',
|
||||||
|
'Star': 'Star',
|
||||||
|
'thumbs-up': 'Thumbs Up',
|
||||||
|
'thumbs-down':'Thumbs Down',
|
||||||
|
'Circle': 'Circle',
|
||||||
|
'Square':'Square',
|
||||||
|
'Check Circle': 'Checkmark',
|
||||||
|
'Smile Outlined': 'Smile',
|
||||||
|
'Hourglass': 'Hourglass',
|
||||||
|
'bell': 'Bell',
|
||||||
|
'Paper Plane': 'Paper Plane',
|
||||||
|
'Comment': 'Chat Bubble',
|
||||||
|
'Trash': 'Trash Can'
|
||||||
|
};
|
||||||
|
|
||||||
//Populate AddField with all available form field types
|
//Populate AddField with all available form field types
|
||||||
$scope.addField = {};
|
$scope.addField = {};
|
||||||
|
@ -67,11 +84,11 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
||||||
/*
|
/*
|
||||||
** FormFields (ui-sortable) drag-and-drop configuration
|
** FormFields (ui-sortable) drag-and-drop configuration
|
||||||
*/
|
*/
|
||||||
$scope.dropzone = {
|
$scope.dropzone = {
|
||||||
handle: ' .handle',
|
handle: '.handle',
|
||||||
containment: '.dropzoneContainer',
|
containment: '.dropzoneContainer',
|
||||||
cursor: 'grabbing'
|
cursor: 'grabbing'
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Field CRUD Methods
|
** Field CRUD Methods
|
||||||
|
@ -79,7 +96,7 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
||||||
// Add a new field
|
// Add a new field
|
||||||
$scope.addNewField = function(modifyForm, fieldType){
|
$scope.addNewField = function(modifyForm, fieldType){
|
||||||
|
|
||||||
// incr field_id counter
|
// increment lastAddedID counter
|
||||||
$scope.addField.lastAddedID++;
|
$scope.addField.lastAddedID++;
|
||||||
var fieldTitle;
|
var fieldTitle;
|
||||||
|
|
||||||
|
@ -98,12 +115,19 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
||||||
disabled: false,
|
disabled: false,
|
||||||
deletePreserved: false
|
deletePreserved: false
|
||||||
};
|
};
|
||||||
// console.log('\n\n---------\nAdded field CLIENT');
|
|
||||||
// console.log(newField);
|
|
||||||
// newField._id = _.uniqueId();
|
|
||||||
|
|
||||||
// put newField into fields array
|
if($scope.showAddOptions(newField)){
|
||||||
|
newField.fieldOptions = [];
|
||||||
|
newField.fieldOptions.push({
|
||||||
|
'option_id' : Math.floor(100000*Math.random()), //Generate pseudo-random option id
|
||||||
|
'option_title' : 'Option 0',
|
||||||
|
'option_value' : 'Option 0'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(modifyForm){
|
if(modifyForm){
|
||||||
|
//Add newField to form_fields array
|
||||||
$scope.myform.form_fields.push(newField);
|
$scope.myform.form_fields.push(newField);
|
||||||
}
|
}
|
||||||
return newField;
|
return newField;
|
||||||
|
@ -168,20 +192,17 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
||||||
// add new option to the field
|
// add new option to the field
|
||||||
$scope.addOption = function(field_index){
|
$scope.addOption = function(field_index){
|
||||||
var currField = $scope.myform.form_fields[field_index];
|
var currField = $scope.myform.form_fields[field_index];
|
||||||
console.log(field_index);
|
//console.log(field_index);
|
||||||
console.log(currField);
|
//console.log(currField);
|
||||||
|
|
||||||
if(currField.fieldType === 'checkbox' || currField.fieldType === 'dropdown' || currField.fieldType === 'radio'){
|
if(currField.fieldType === 'checkbox' || currField.fieldType === 'dropdown' || currField.fieldType === 'radio'){
|
||||||
if(!currField.fieldOptions) $scope.myform.form_fields[field_index].fieldOptions = [];
|
if(!currField.fieldOptions){
|
||||||
|
$scope.myform.form_fields[field_index].fieldOptions = [];
|
||||||
|
}
|
||||||
|
|
||||||
var lastOptionID = 0;
|
var lastOptionID = $scope.myform.form_fields[field_index].fieldOptions.length+1;
|
||||||
|
|
||||||
if(currField.fieldOptions[currField.fieldOptions.length-1]){
|
|
||||||
lastOptionID = currField.fieldOptions[currField.fieldOptions.length-1].option_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// new option's id
|
// new option's id
|
||||||
var option_id = lastOptionID + 1;
|
|
||||||
|
|
||||||
var newOption = {
|
var newOption = {
|
||||||
'option_id' : Math.floor(100000*Math.random()),
|
'option_id' : Math.floor(100000*Math.random()),
|
||||||
|
@ -219,7 +240,17 @@ angular.module('forms').directive('editFormDirective', ['$rootScope', 'FormField
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
// decides whether field options block will be shown (true for dropdown and radio fields)
|
||||||
|
$scope.showRatingOptions = function (field){
|
||||||
|
if(field.fieldType === 'rating'){
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,6 +272,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row field">
|
||||||
|
<div class="field-title col-sm-4">
|
||||||
|
<h5>Google Analytics Tracking Code</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type="text"
|
||||||
|
ng-model="myform.analytics.gaCode"
|
||||||
|
value="{{myform.analytics.gaCode}}"
|
||||||
|
style="width: 100%;"
|
||||||
|
ng-minlength="4"
|
||||||
|
placeholder="UA-XXXXX-Y"
|
||||||
|
ng-pattern="/\bUA-\d{4,10}-\d{1,4}\b/">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row field">
|
<div class="row field">
|
||||||
<div class="col-xs-6 field-title">Language</div>
|
<div class="col-xs-6 field-title">Language</div>
|
||||||
<div class="col-xs-4 field-input">
|
<div class="col-xs-4 field-input">
|
||||||
|
|
|
@ -220,13 +220,13 @@
|
||||||
|
|
||||||
<div class="row"><br></div>
|
<div class="row"><br></div>
|
||||||
|
|
||||||
<div class="row description">
|
<div class="row description" ng-hide="showRatingOptions(field)">
|
||||||
<div class="col-md-4 col-sm-12">Description:</div>
|
<div class="col-md-4 col-sm-12">Description:</div>
|
||||||
<div class="col-md-8 col-sm-12"><textarea type="text" ng-model="field.description" name="description{{field._id}}"value="{{field.description}}"></textarea> </div>
|
<div class="col-md-8 col-sm-12"><textarea type="text" ng-model="field.description" name="description{{field._id}}"value="{{field.description}}"></textarea> </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" ng-show="showAddOptions(field)"><br></div>
|
<div class="row" ng-show="showAddOptions(field)"><br></div>
|
||||||
<div class="row options" ng-show="showAddOptions(field)">
|
<div class="row options" ng-if="showAddOptions(field)">
|
||||||
<div class="col-md-4 col-xs-12">Options:</div>
|
<div class="col-md-4 col-xs-12">Options:</div>
|
||||||
<div class="col-md-8 col-xs-12">
|
<div class="col-md-8 col-xs-12">
|
||||||
<div ng-repeat="option in field.fieldOptions track by option.option_id" class="row">
|
<div ng-repeat="option in field.fieldOptions track by option.option_id" class="row">
|
||||||
|
@ -244,6 +244,32 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row" ng-show="showRatingOptions(field)"><br></div>
|
||||||
|
<div class="row" ng-if="showRatingOptions(field)">
|
||||||
|
<div class="col-md-9 col-sm-9">Number of Steps:</div>
|
||||||
|
<div class="col-md-3 col-sm-3">
|
||||||
|
<input style="width:100%" type="number"
|
||||||
|
min="1" max="10"
|
||||||
|
ng-model="field.ratingOptions.steps"
|
||||||
|
name="ratingOptions_steps{{field._id}}"
|
||||||
|
ng-value="{{field.ratingOptions.steps}}"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="col-md-5 col-sm-9">Shape:</div>
|
||||||
|
<div class="col-md-7 col-sm-3">
|
||||||
|
<select style="width:100%" ng-model="field.ratingOptions.shape"
|
||||||
|
value="{{field.ratingOptions.steps}}"
|
||||||
|
name="ratingOptions_shape{{field._id}}" required>
|
||||||
|
<option ng-repeat="shapeType in field.ratingOptions.validShapes"
|
||||||
|
value="{{shapeType}}">
|
||||||
|
{{select2FA[shapeType]}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row"><br></div>
|
<div class="row"><br></div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -1,5 +1,47 @@
|
||||||
<div class="submissions-table row container" ng-init="initFormSubmissions()">
|
<div class="submissions-table row container" ng-init="initFormSubmissions()">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-4">
|
||||||
|
Total Views: {{myform.analytics.views}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xs-4">
|
||||||
|
Submissions: {{myform.analytics.submissions}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xs-4">
|
||||||
|
Conversion Rate: {{myform.analytics.conversionRate}}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
|
||||||
|
<div class="col-xs-2">
|
||||||
|
<strong>Field Title</strong>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-2">
|
||||||
|
<strong>Field Views</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<strong>User dropoff rate at this field</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-12" ng-repeat="fieldStats in myform.analytics.fields">
|
||||||
|
|
||||||
|
<div class="col-xs-2">
|
||||||
|
{{fieldStats.field.title}}
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-2">
|
||||||
|
{{fieldStats.totalViews}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xs-4">
|
||||||
|
{{fieldStats.dropoffRate}}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-2">
|
<div class="col-xs-2">
|
||||||
<button class="btn btn-danger" ng-click="deleteSelectedSubmissions()" ng-disabled="!isAtLeastOneChecked();">
|
<button class="btn btn-danger" ng-click="deleteSelectedSubmissions()" ng-disabled="!isAtLeastOneChecked();">
|
||||||
|
@ -69,11 +111,8 @@
|
||||||
<th class="scope">
|
<th class="scope">
|
||||||
{{$index+1}}
|
{{$index+1}}
|
||||||
</th>
|
</th>
|
||||||
<td ng-if="field.fieldType == 'dropdown'" data-ng-repeat="field in row.form_fields">
|
|
||||||
{{field.fieldValue.field_title}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td ng-if="field.fieldType != 'dropdown'" data-ng-repeat="field in row.form_fields">
|
<td data-ng-repeat="field in row.form_fields">
|
||||||
{{field.fieldValue}}
|
{{field.fieldValue}}
|
||||||
</td>
|
</td>
|
||||||
<td ng-if="myform.plugins.oscarhost.baseUrl">
|
<td ng-if="myform.plugins.oscarhost.baseUrl">
|
||||||
|
|
|
@ -238,12 +238,6 @@ form .row.field {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .row.field.dropdown > .field-input .ui-select-match {
|
|
||||||
border: 0 grey solid;
|
|
||||||
border-width: 0 0 2px 0;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form .dropdown > .field-input .ui-select-choices-row-inner {
|
form .dropdown > .field-input .ui-select-choices-row-inner {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
|
|
|
@ -28,6 +28,6 @@ angular.module('forms').directive('fieldIconDirective', function() {
|
||||||
'number': 'fa fa-slack'
|
'number': 'fa fa-slack'
|
||||||
};
|
};
|
||||||
$scope.typeIcon = iconTypeMap[$scope.typeName];
|
$scope.typeIcon = iconTypeMap[$scope.typeName];
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
|
@ -13,9 +13,24 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
|
||||||
|
|
||||||
var getTemplateUrl = function(fieldType) {
|
var getTemplateUrl = function(fieldType) {
|
||||||
var type = fieldType;
|
var type = fieldType;
|
||||||
var templateUrl = 'modules/forms/base/views/directiveViews/field/';
|
|
||||||
|
|
||||||
if (__indexOf.call(supportedFields, type) >= 0) {
|
var supported_fields = [
|
||||||
|
'textfield',
|
||||||
|
'textarea',
|
||||||
|
'date',
|
||||||
|
'dropdown',
|
||||||
|
'hidden',
|
||||||
|
'password',
|
||||||
|
'radio',
|
||||||
|
'legal',
|
||||||
|
'statement',
|
||||||
|
'rating',
|
||||||
|
'yes_no',
|
||||||
|
'number',
|
||||||
|
'natural'
|
||||||
|
];
|
||||||
|
if (__indexOf.call(supported_fields, type) >= 0) {
|
||||||
|
var templateUrl = 'modules/forms/views/directiveViews/field/';
|
||||||
templateUrl = templateUrl+type+'.html';
|
templateUrl = templateUrl+type+'.html';
|
||||||
}
|
}
|
||||||
return $templateCache.get(templateUrl);
|
return $templateCache.get(templateUrl);
|
||||||
|
@ -24,7 +39,7 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
|
||||||
return {
|
return {
|
||||||
template: '<div>{{field.title}}</div>',
|
template: '<div>{{field.title}}</div>',
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
field: '=',
|
field: '=',
|
||||||
required: '&',
|
required: '&',
|
||||||
design: '=',
|
design: '=',
|
||||||
|
@ -85,7 +100,7 @@ angular.module('forms').directive('fieldDirective', ['$http', '$compile', '$root
|
||||||
}
|
}
|
||||||
var template = getTemplateUrl(fieldType);
|
var template = getTemplateUrl(fieldType);
|
||||||
element.html(template).show();
|
element.html(template).show();
|
||||||
$compile(element.contents())(scope);
|
var output = $compile(element.contents())(scope);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
//TODO: DAVID: Need to refactor this
|
||||||
angular.module('forms').directive('onEnterKey', ['$rootScope', function($rootScope){
|
angular.module('forms').directive('onEnterKey', ['$rootScope', function($rootScope){
|
||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
link: function($scope, $element, $attrs) {
|
link: function($scope, $element, $attrs) {
|
||||||
$element.bind('keydown keypress', function(event) {
|
$element.bind('keydown keypress', function(event) {
|
||||||
|
|
||||||
var keyCode = event.which || event.keyCode;
|
var keyCode = event.which || event.keyCode;
|
||||||
if(keyCode === 13 && !event.shiftKey) {
|
|
||||||
|
var onEnterKeyDisabled = false;
|
||||||
|
if($attrs.onEnterKeyDisabled !== null) onEnterKeyDisabled = $attrs.onEnterKeyDisabled;
|
||||||
|
|
||||||
|
if(keyCode === 13 && !event.shiftKey && !onEnterKeyDisabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$rootScope.$apply(function() {
|
$rootScope.$apply(function() {
|
||||||
$rootScope.$eval($attrs.onEnterKey);
|
$rootScope.$eval($attrs.onEnterKey);
|
||||||
|
@ -15,4 +21,56 @@ angular.module('forms').directive('onEnterKey', ['$rootScope', function($rootSco
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}]).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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('forms').directive('submitFormDirective',
|
|
||||||
['$http', 'TimeCounter', '$filter', '$rootScope', 'Auth',
|
angular.module('forms').directive('submitFormDirective', ['$http', 'TimeCounter', '$filter', '$rootScope', 'Auth', 'SendVisitorData',
|
||||||
function ($http, TimeCounter, $filter, $rootScope, Auth) {
|
function ($http, TimeCounter, $filter, $rootScope, Auth, SendVisitorData) {
|
||||||
return {
|
return {
|
||||||
templateUrl: 'modules/forms/base/views/directiveViews/form/submit-form.client.view.html', restrict: 'E',
|
templateUrl: 'modules/forms/base/views/directiveViews/form/submit-form.client.view.html', restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
|
@ -49,6 +49,7 @@ angular.module('forms').directive('submitFormDirective',
|
||||||
TimeCounter.restartClock();
|
TimeCounter.restartClock();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Fire event when window is scrolled
|
||||||
$window.onscroll = function(){
|
$window.onscroll = function(){
|
||||||
$scope.scrollPos = document.body.scrollTop || document.documentElement.scrollTop || 0;
|
$scope.scrollPos = document.body.scrollTop || document.documentElement.scrollTop || 0;
|
||||||
var elemBox = document.getElementsByClassName('activeField')[0].getBoundingClientRect();
|
var elemBox = document.getElementsByClassName('activeField')[0].getBoundingClientRect();
|
||||||
|
@ -88,13 +89,22 @@ angular.module('forms').directive('submitFormDirective',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$rootScope.setDropdownOption = function(){
|
|
||||||
console.log('setDropdownOption index: ');
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Field Controls
|
** Field Controls
|
||||||
*/
|
*/
|
||||||
|
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) {
|
$scope.setActiveField = $rootScope.setActiveField = function(field_id, field_index, animateScroll) {
|
||||||
if($scope.selected === null || $scope.selected._id === field_id){
|
if($scope.selected === null || $scope.selected._id === field_id){
|
||||||
//console.log('not scrolling');
|
//console.log('not scrolling');
|
||||||
|
@ -121,12 +131,15 @@ angular.module('forms').directive('submitFormDirective',
|
||||||
$document.scrollToElement(angular.element('.activeField'), -10, 200).then(function() {
|
$document.scrollToElement(angular.element('.activeField'), -10, 200).then(function() {
|
||||||
$scope.noscroll = false;
|
$scope.noscroll = false;
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
if (document.querySelectorAll('.activeField .focusOn')[0]) {
|
if (document.querySelectorAll('.activeField .focusOn').length) {
|
||||||
//console.log(document.querySelectorAll('.activeField .focusOn')[0]);
|
//Handle default case
|
||||||
document.querySelectorAll('.activeField .focusOn')[0].focus();
|
document.querySelectorAll('.activeField .focusOn')[0].focus();
|
||||||
} else {
|
} else if(document.querySelectorAll('.activeField input').length) {
|
||||||
//console.log(document.querySelectorAll('.activeField input')[0]);
|
//Handle case for rating input
|
||||||
document.querySelectorAll('.activeField input')[0].focus();
|
document.querySelectorAll('.activeField input')[0].focus();
|
||||||
|
} else {
|
||||||
|
//Handle case for dropdown input
|
||||||
|
document.querySelectorAll('.activeField .selectize-input')[0].focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -134,13 +147,15 @@ angular.module('forms').directive('submitFormDirective',
|
||||||
}else {
|
}else {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
if (document.querySelectorAll('.activeField .focusOn')[0]) {
|
if (document.querySelectorAll('.activeField .focusOn')[0]) {
|
||||||
//console.log(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();
|
document.querySelectorAll('.activeField .focusOn')[0].focus();
|
||||||
} else {
|
} else {
|
||||||
document.querySelectorAll('.activeField input')[0].focus();
|
document.querySelectorAll('.activeField input')[0].focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SendVisitorData.send($scope.myform, getActiveField(), TimeCounter.getTimeElapsed());
|
||||||
};
|
};
|
||||||
|
|
||||||
$rootScope.nextField = $scope.nextField = function(){
|
$rootScope.nextField = $scope.nextField = function(){
|
||||||
|
@ -178,30 +193,39 @@ angular.module('forms').directive('submitFormDirective',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.goToInvalid = function() {
|
$rootScope.goToInvalid = $scope.goToInvalid = function() {
|
||||||
document.querySelectorAll('.ng-invalid.focusOn')[0].focus();
|
document.querySelectorAll('.ng-invalid.focusOn')[0].focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.submitForm = function() {
|
$rootScope.submitForm = $scope.submitForm = function() {
|
||||||
|
|
||||||
var _timeElapsed = TimeCounter.stopClock();
|
var _timeElapsed = TimeCounter.stopClock();
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
|
||||||
var form = _.cloneDeep($scope.myform);
|
var form = _.cloneDeep($scope.myform);
|
||||||
|
|
||||||
form.timeElapsed = _timeElapsed;
|
form.timeElapsed = _timeElapsed;
|
||||||
|
|
||||||
form.percentageComplete = $filter('formValidity')($scope.myform) / $scope.myform.visible_form_fields.length * 100;
|
form.percentageComplete = $filter('formValidity')($scope.myform) / $scope.myform.visible_form_fields.length * 100;
|
||||||
delete form.visible_form_fields;
|
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 () {
|
setTimeout(function () {
|
||||||
$scope.submitPromise = $http.post('/forms/' + $scope.myform._id, form)
|
$scope.submitPromise = $http.post('/forms/' + $scope.myform._id, form)
|
||||||
.success(function (data, status, headers) {
|
.success(function (data, status, headers) {
|
||||||
//console.log('form submitted successfully');
|
console.log($scope.myform.form_fields[0]);
|
||||||
|
|
||||||
$scope.myform.submitted = true;
|
$scope.myform.submitted = true;
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
|
SendVisitorData.send($scope.myform, getActiveField(), _timeElapsed);
|
||||||
})
|
})
|
||||||
.error(function (error) {
|
.error(function (error) {
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
//console.log(error);
|
console.error(error);
|
||||||
$scope.error = error.message;
|
$scope.error = error.message;
|
||||||
});
|
});
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
|
@ -8,7 +8,7 @@ angular.module('forms').factory('Forms', ['$resource', 'FORM_URL',
|
||||||
}, {
|
}, {
|
||||||
'query' : {
|
'query' : {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
isArray: true,
|
isArray: true
|
||||||
//DAVID: TODO: Do we really need to get visible_form_fields for a Query?
|
//DAVID: TODO: Do we really need to get visible_form_fields for a Query?
|
||||||
// transformResponse: function(data, header) {
|
// transformResponse: function(data, header) {
|
||||||
// var forms = angular.fromJson(data);
|
// var forms = angular.fromJson(data);
|
||||||
|
@ -24,8 +24,8 @@ angular.module('forms').factory('Forms', ['$resource', 'FORM_URL',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
transformResponse: function(data, header) {
|
transformResponse: function(data, header) {
|
||||||
var form = angular.fromJson(data);
|
var form = angular.fromJson(data);
|
||||||
//console.log(form);
|
|
||||||
form.visible_form_fields = _.filter(form.form_fields, function(field){
|
form.visible_form_fields = _.filter(form.form_fields, function(field){
|
||||||
return (field.deletePreserved === false);
|
return (field.deletePreserved === false);
|
||||||
});
|
});
|
||||||
return form;
|
return form;
|
||||||
|
|
|
@ -2,22 +2,29 @@
|
||||||
|
|
||||||
angular.module('forms').service('TimeCounter', [
|
angular.module('forms').service('TimeCounter', [
|
||||||
function(){
|
function(){
|
||||||
var _startTime, _endTime, that=this;
|
var _startTime, _endTime = null, that=this;
|
||||||
|
|
||||||
this.timeSpent = 0;
|
this.timeSpent = 0;
|
||||||
|
|
||||||
this.restartClock = function(){
|
this.restartClock = function(){
|
||||||
_startTime = Date.now();
|
_startTime = Date.now();
|
||||||
_endTime = _startTime;
|
_endTime = null;
|
||||||
// console.log('Clock Started');
|
// console.log('Clock Started');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.getTimeElapsed = function(){
|
||||||
|
if(_startTime) {
|
||||||
|
return Math.abs(Date.now().valueOf() - _startTime.valueOf()) / 1000;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.stopClock = function(){
|
this.stopClock = function(){
|
||||||
if(_startTime){
|
if(_startTime && _endTime === null){
|
||||||
_endTime = Date.now();
|
_endTime = Date.now();
|
||||||
that.timeSpent = Math.abs(_endTime.valueOf() - _startTime.valueOf())/1000;
|
this.timeSpent = Math.abs(_endTime.valueOf() - _startTime.valueOf())/1000;
|
||||||
// console.log('Clock Ended');
|
this._startTime = this._endTime = null;
|
||||||
return that.timeSpent;
|
|
||||||
|
return this.timeSpent;
|
||||||
}else{
|
}else{
|
||||||
return new Error('Clock has not been started');
|
return new Error('Clock has not been started');
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,13 @@
|
||||||
{{field.title}}
|
{{field.title}}
|
||||||
<span class="required-error" ng-show="!field.required && !field.fieldValue">{{ 'OPTIONAL' | translate }}</span>
|
<span class="required-error" ng-show="!field.required && !field.fieldValue">{{ 'OPTIONAL' | translate }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
<p class="col-xs-12">
|
||||||
|
<small>{{field.description}}</small>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<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 ng-focus="setActiveField(field._id, index, true)"
|
<input class="focusOn"
|
||||||
class="focusOn"
|
|
||||||
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"
|
||||||
|
@ -22,7 +24,9 @@
|
||||||
ng-required="field.required"
|
ng-required="field.required"
|
||||||
ng-disabled="field.disabled"
|
ng-disabled="field.disabled"
|
||||||
placeholder="MM/DD/YYYY"
|
placeholder="MM/DD/YYYY"
|
||||||
on-enter-key="nextField()"
|
ng-focus="setActiveField(field._id, index, true)"
|
||||||
|
on-tab-key="nextField()"
|
||||||
|
on-tab-and-shift-key="prevField()"
|
||||||
ng-change="$root.nextField()">
|
ng-change="$root.nextField()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<div class="field row dropdown"
|
<div class="field row dropdown"
|
||||||
ng-click="setActiveField(field._id, index, true)"
|
|
||||||
ng-if="field.fieldOptions.length > 0">
|
ng-if="field.fieldOptions.length > 0">
|
||||||
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -10,15 +9,22 @@
|
||||||
{{field.title}}
|
{{field.title}}
|
||||||
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
<p class="col-xs-12">
|
||||||
|
<small>{{field.description}}</small>
|
||||||
|
</p>
|
||||||
</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"
|
||||||
theme="selectize"
|
theme="selectize"
|
||||||
|
search-enabled="true"
|
||||||
|
search-by="option_value"
|
||||||
|
set-search-to-answer="true"
|
||||||
ng-required="field.required"
|
ng-required="field.required"
|
||||||
ng-disabled="field.disabled"
|
ng-disabled="field.disabled"
|
||||||
|
on-tab-and-shift-key="prevField()"
|
||||||
|
on-tab-key="nextField()"
|
||||||
ng-change="$root.nextField()">
|
ng-change="$root.nextField()">
|
||||||
<ui-select-match placeholder="Type or select an option">
|
<ui-select-match placeholder="Type or select an option">
|
||||||
{{$select.selected.option_value}}
|
|
||||||
</ui-select-match>
|
</ui-select-match>
|
||||||
<ui-select-choices repeat="option in field.fieldOptions | filter: $select.search"
|
<ui-select-choices repeat="option in field.fieldOptions | filter: $select.search"
|
||||||
ng-class="{'active': option.option_value === field.fieldValue }">
|
ng-class="{'active': option.option_value === field.fieldValue }">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="field row radio legal"
|
<div class="field row radio legal"
|
||||||
on-enter-key="nextField()"
|
on-enter-or-tab-key="nextField()"
|
||||||
key-to-truthy key-char-truthy="y" key-char-falsey="n" field="field">
|
key-to-truthy key-char-truthy="y" key-char-falsey="n" field="field">
|
||||||
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -11,10 +11,12 @@
|
||||||
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
<br>
|
<br>
|
||||||
<p style="color:#ddd;">{{field.description}}</p>
|
<p class="col-xs-12">{{field.description}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 field-input container">
|
<div class="col-xs-12 field-input container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid"
|
||||||
|
on-enter-or-tab-key="nextField()"
|
||||||
|
on-tab-and-shift-key="prevField()">
|
||||||
<label class="btn col-md-5 col-xs-12"
|
<label class="btn col-md-5 col-xs-12"
|
||||||
ng-class="{activeBtn: field.fieldValue == 'true'}">
|
ng-class="{activeBtn: field.fieldValue == 'true'}">
|
||||||
<input class="focusOn"
|
<input class="focusOn"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="field row radio"
|
<div class="field row radio"
|
||||||
on-enter-key="nextField()"
|
on-enter-or-tab-key="nextField()"
|
||||||
key-to-option field="field"
|
key-to-option field="field"
|
||||||
ng-if="field.fieldOptions.length > 0">
|
ng-if="field.fieldOptions.length > 0">
|
||||||
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
||||||
|
@ -11,6 +11,9 @@
|
||||||
{{field.title}}
|
{{field.title}}
|
||||||
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
<p class="col-xs-12">
|
||||||
|
<small>{{field.description}}</small>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 field-input">
|
<div class="col-xs-12 field-input">
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="textfield field row"
|
<div class="textfield field row"
|
||||||
on-enter-key="nextField()">
|
on-enter-or-tab-key="nextField()">
|
||||||
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
||||||
<h3>
|
<h3>
|
||||||
<small class="field-number">
|
<small class="field-number">
|
||||||
|
@ -8,19 +8,27 @@
|
||||||
</small>
|
</small>
|
||||||
{{field.title}}
|
{{field.title}}
|
||||||
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
||||||
|
|
||||||
</h3>
|
</h3>
|
||||||
|
<p class="col-xs-12">
|
||||||
|
<small>{{field.description}}</small>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 field-input">
|
<div class="col-xs-12 field-input">
|
||||||
<input-stars max="5"
|
|
||||||
|
<input-stars max="{{field.ratingOptions.steps}}"
|
||||||
ng-init="field.fieldValue = 1"
|
ng-init="field.fieldValue = 1"
|
||||||
on-star-click="$root.nextField()"
|
on-star-click="$root.nextField()"
|
||||||
icon-full="fa-star"
|
icon-full="{{field.ratingOptions.shape}}"
|
||||||
icon-base="fa fa-3x"
|
icon-base="fa fa-3x"
|
||||||
icon-empty="fa-star-o"
|
icon-empty="{{field.ratingOptions.shape}}"
|
||||||
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"
|
||||||
ng-disabled="field.disabled"
|
ng-disabled="field.disabled"
|
||||||
|
on-enter-or-tab-key="nextField()"
|
||||||
|
on-tab-and-shift-key="prevField()"
|
||||||
|
ng-focus="setActiveField(field._id, index, true)"
|
||||||
class="angular-input-stars focusOn">
|
class="angular-input-stars focusOn">
|
||||||
</input-stars>
|
</input-stars>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
<div class="statement field row"
|
<div class="statement field row"
|
||||||
on-enter-key="$root.nextField()"
|
on-enter-or-tab-key="nextField()"
|
||||||
|
on-tab-and-shift-key="prevField()"
|
||||||
ng-focus="setActiveField(field._id, index, true)">
|
ng-focus="setActiveField(field._id, index, true)">
|
||||||
<div class="row field-title field-title">
|
<div class="row field-title field-title">
|
||||||
<div class="col-xs-1"><i class="fa fa-quote-left fa-1"></i></div>
|
<div class="col-xs-1"><i class="fa fa-quote-left fa-1"></i></div>
|
||||||
<h2 class="text-left col-xs-9">{{field.title}}</h2>
|
<h2 class="text-left col-xs-9">{{field.title}}</h2>
|
||||||
|
<p class="col-xs-12">
|
||||||
|
<small>{{field.description}}</small>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row field-title field-input">
|
<div class="row field-title field-input">
|
||||||
<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>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<div class="field row" ng-click="setActiveField(field._id, index, true)"
|
<div class="field row" ng-click="setActiveField(field._id, index, true)">
|
||||||
ng-focus="setActiveField(field._id, index, true)">
|
|
||||||
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
||||||
<h3>
|
<h3>
|
||||||
<small class="field-number">
|
<small class="field-number">
|
||||||
|
@ -10,8 +9,12 @@
|
||||||
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
<span class="required-error" ng-show="!field.required">{{ 'OPTIONAL' | translate }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
<small>{{ 'NEWLINE' | translate }}</small>
|
<small>{{ 'NEWLINE' | translate }}</small>
|
||||||
|
<p>
|
||||||
|
<small>{{field.description}}</small>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<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>
|
||||||
<textarea class="textarea focusOn" type="text"
|
<textarea class="textarea focusOn" type="text"
|
||||||
ng-model="field.fieldValue"
|
ng-model="field.fieldValue"
|
||||||
ng-model-options="{ debounce: 250 }"
|
ng-model-options="{ debounce: 250 }"
|
||||||
|
@ -19,14 +22,16 @@
|
||||||
value="{{field.fieldValue}}"
|
value="{{field.fieldValue}}"
|
||||||
ng-required="field.required"
|
ng-required="field.required"
|
||||||
ng-disabled="field.disabled"
|
ng-disabled="field.disabled"
|
||||||
ng-focus="setActiveField(field._id, index, true)"
|
ng-focus="setActiveField(field._id, index, true)"
|
||||||
on-enter-key="nextField()">
|
on-enter-or-tab-key="nextField()"
|
||||||
|
on-tab-and-shift-key="prevField()"
|
||||||
|
style="border: none; border-left: lightgrey dashed 2px;">
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="btn btn-lg btn-default col-xs-12 col-sm-4"
|
<div class="btn btn-lg btn-default col-xs-12 col-sm-4 hidden-xs"
|
||||||
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 || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
|
<button ng-disabled="!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
|
||||||
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
|
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="textfield field row"
|
<div class="textfield field row"
|
||||||
ng-click="setActiveField(field._id, index, true)">
|
ng-click="setActiveField(field._id, index, true)">
|
||||||
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
<div class="col-xs-12 field-title row-fluid" ng-style="{'color': design.colors.questionColor}">
|
||||||
<h3>
|
<h3 class="col-xs-12">
|
||||||
<small class="field-number">
|
<small class="field-number">
|
||||||
{{index+1}}
|
{{index+1}}
|
||||||
<i class="fa fa-angle-double-right" aria-hidden="true"></i>
|
<i class="fa fa-angle-double-right" aria-hidden="true"></i>
|
||||||
|
@ -13,10 +13,13 @@
|
||||||
({{ 'OPTIONAL' | translate }})
|
({{ 'OPTIONAL' | translate }})
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<p class="col-xs-12">
|
||||||
|
<small>{{field.description}}</small>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 field-input">
|
<div class="col-xs-12 field-input">
|
||||||
<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, index, true)"
|
|
||||||
name="{{field.fieldType}}{{index}}"
|
name="{{field.fieldType}}{{index}}"
|
||||||
type="{{field.input_type}}"
|
type="{{field.input_type}}"
|
||||||
ng-pattern="field.validateRegex"
|
ng-pattern="field.validateRegex"
|
||||||
|
@ -26,10 +29,15 @@
|
||||||
ng-model="field.fieldValue"
|
ng-model="field.fieldValue"
|
||||||
ng-model-options="{ debounce: 250 }"
|
ng-model-options="{ debounce: 250 }"
|
||||||
value="field.fieldValue"
|
value="field.fieldValue"
|
||||||
|
ng-model="field.fieldValue"
|
||||||
|
ng-model-options="{ debounce: 250 }"
|
||||||
|
value="field.fieldValue"
|
||||||
|
ng-focus="setActiveField(field._id, index, true)"
|
||||||
|
on-enter-or-tab-key="nextField()"
|
||||||
|
on-tab-and-shift-key="prevField()"
|
||||||
ng-required="field.required"
|
ng-required="field.required"
|
||||||
ng-disabled="field.disabled"
|
ng-disabled="field.disabled"
|
||||||
aria-describedby="inputError2Status"
|
aria-describedby="inputError2Status">
|
||||||
on-enter-key="nextField()">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<div ng-show="forms.myForm.{{field.fieldType}}{{index}}.$invalid && !!forms.myForm.{{field.fieldType}}{{index}}.$viewValue " class="alert alert-danger" role="alert">
|
<div ng-show="forms.myForm.{{field.fieldType}}{{index}}.$invalid && !!forms.myForm.{{field.fieldType}}{{index}}.$viewValue " class="alert alert-danger" role="alert">
|
||||||
|
@ -42,7 +50,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="btn btn-lg btn-default col-xs-12 col-sm-4"
|
<div class="btn btn-lg btn-default col-xs-12 col-sm-4 hidden-xs"
|
||||||
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 || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
|
<button ng-disabled="!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid"
|
||||||
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
|
ng-style="{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="field row radio"
|
<div class="field row radio"
|
||||||
ng-click="setActiveField(field._id, index, true)"
|
ng-click="setActiveField(field._id, index, true)"
|
||||||
on-enter-key="nextField()"
|
on-tab-and-shift-key="prevField()"
|
||||||
key-to-truthy key-char-truthy="y" key-char-falsey="n" field="field">
|
key-to-truthy key-char-truthy="y" key-char-falsey="n" field="field">
|
||||||
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
<div class="col-xs-12 field-title" ng-style="{'color': design.colors.questionColor}">
|
||||||
<h3 class="row">
|
<h3 class="row">
|
||||||
|
@ -25,8 +25,8 @@
|
||||||
<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, index, true)"
|
|
||||||
ng-model="field.fieldValue"
|
ng-model="field.fieldValue"
|
||||||
|
ng-focus="setActiveField(field._id, index, true)"
|
||||||
ng-model-options="{ debounce: 250 }"
|
ng-model-options="{ debounce: 250 }"
|
||||||
ng-required="field.required"
|
ng-required="field.required"
|
||||||
ng-change="$root.nextField()"
|
ng-change="$root.nextField()"
|
||||||
|
@ -45,7 +45,6 @@
|
||||||
|
|
||||||
<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, index, true)"
|
|
||||||
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"
|
||||||
|
|
|
@ -53,6 +53,7 @@ ng-style="{'color':button.color}">
|
||||||
data-id="{{field._id}}"
|
data-id="{{field._id}}"
|
||||||
ng-class="{activeField: selected._id == field._id }"
|
ng-class="{activeField: selected._id == field._id }"
|
||||||
class="row field-directive">
|
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>
|
||||||
|
@ -76,8 +77,10 @@ ng-style="{'color':button.color}">
|
||||||
<button ng-if="!forms.myForm.$invalid"
|
<button ng-if="!forms.myForm.$invalid"
|
||||||
class="Button btn col-sm-2 col-xs-8 focusOn"
|
class="Button btn col-sm-2 col-xs-8 focusOn"
|
||||||
v-busy="loading" v-busy-label="Please wait" v-pressable
|
v-busy="loading" v-busy-label="Please wait" v-pressable
|
||||||
ng-disabled="loading"
|
ng-disabled="loading || forms.myForm.$invalid"
|
||||||
ng-click="submitForm()"
|
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}"
|
ng-style="{'background-color':myform.design.colors.buttonColor, 'color':myform.design.colors.buttonTextColor}"
|
||||||
style="font-size: 1.6em; margin-left: 1em; margin-top: 1em;">
|
style="font-size: 1.6em; margin-left: 1em; margin-top: 1em;">
|
||||||
|
|
||||||
|
@ -85,8 +88,10 @@ ng-style="{'color':button.color}">
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button ng-if="forms.myForm.$invalid"
|
<button ng-if="forms.myForm.$invalid"
|
||||||
class="Button btn col-sm-2 col-xs-8"
|
class="Button btn col-sm-2 col-xs-8 focusOn"
|
||||||
ng-click="goToInvalid()"
|
ng-click="goToInvalid()"
|
||||||
|
on-enter-key="goToInvalid()"
|
||||||
|
on-enter-key-disabled="!forms.myForm.$invalid"
|
||||||
style="font-size: 1.6em; margin-left: 1em; margin-top: 1em; background-color:#990000; color:white">
|
style="font-size: 1.6em; margin-left: 1em; margin-top: 1em; background-color:#990000; color:white">
|
||||||
{{ 'REVIEW' | translate }}
|
{{ 'REVIEW' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,4 +1,17 @@
|
||||||
|
|
||||||
|
|
||||||
<section class="public-form" ng-style="{ 'background-color': myform.design.colors.backgroundColor }">
|
<section class="public-form" ng-style="{ 'background-color': myform.design.colors.backgroundColor }">
|
||||||
<submit-form-directive myform="myform"></submit-form-directive>
|
<submit-form-directive myform="myform"></submit-form-directive>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- User's Google Analytics -->
|
||||||
|
<script ng-if="myform.analytics.gaCode">
|
||||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
|
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||||
|
|
||||||
|
ga('create', '{{myform.analytics.gaCode}}', 'auto');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
</script>
|
||||||
|
<!-- End Google Analytics -->
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Create the SendVisitorData service
|
||||||
|
angular
|
||||||
|
.module('forms')
|
||||||
|
.factory('SendVisitorData', SendVisitorData);
|
||||||
|
|
||||||
|
SendVisitorData.$inject = ['Socket', '$state'];
|
||||||
|
|
||||||
|
function SendVisitorData(Socket, $state) {
|
||||||
|
|
||||||
|
// Create a controller method for sending visitor data
|
||||||
|
function send(form, lastActiveIndex, timeElapsed) {
|
||||||
|
|
||||||
|
console.log(lastActiveIndex);
|
||||||
|
// Create a new message object
|
||||||
|
var visitorData = {
|
||||||
|
referrer: document.referrer,
|
||||||
|
isSubmitted: form.submitted,
|
||||||
|
formId: form._id,
|
||||||
|
lastActiveField: form.form_fields[lastActiveIndex]._id,
|
||||||
|
timeElapsed: timeElapsed
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
|
@ -68,7 +68,7 @@ angular.module('NodeForm.templates', []).run(['$templateCache', function($templa
|
||||||
"<div class=\"field row\" ng-click=\"setActiveField(field._id, index, true)\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\"><h3><span class=\"fa fa-angle-double-right\"></span> {{field.title}} <span class=required-error ng-show=\"field.required && !field.fieldValue\">*(required)</span></h3></div><div class=\"col-xs-12 field-input\"><textarea class=textarea type=text ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" ng-class=\"{ 'no-border': !!field.fieldValue }\" value={{field.fieldValue}} class=focusOn ng-required=field.required ng-disabled=field.disabled ng-focus=\"setActiveField(field._id, index, true)\">\n" +
|
"<div class=\"field row\" ng-click=\"setActiveField(field._id, index, true)\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\"><h3><span class=\"fa fa-angle-double-right\"></span> {{field.title}} <span class=required-error ng-show=\"field.required && !field.fieldValue\">*(required)</span></h3></div><div class=\"col-xs-12 field-input\"><textarea class=textarea type=text ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" ng-class=\"{ 'no-border': !!field.fieldValue }\" value={{field.fieldValue}} class=focusOn ng-required=field.required ng-disabled=field.disabled ng-focus=\"setActiveField(field._id, index, true)\">\n" +
|
||||||
" </textarea></div></div><div class=\"col-xs-12 row\"><div class=\"btn btn-lg btn-default row-fluid\" style=\"padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)\"><button ng-disabled=!field.fieldValue ng-click=$root.nextField() ng-style=\"{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}\" class=\"btn col-sm-5 col-xs-5\">OK <i class=\"fa fa-check\"></i></button><div class=\"col-sm-3 col-xs-6\" style=margin-top:0.2em><small style=\"color:#ddd; font-size:70%\">press ENTER</small></div></div></div>");
|
" </textarea></div></div><div class=\"col-xs-12 row\"><div class=\"btn btn-lg btn-default row-fluid\" style=\"padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)\"><button ng-disabled=!field.fieldValue ng-click=$root.nextField() ng-style=\"{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}\" class=\"btn col-sm-5 col-xs-5\">OK <i class=\"fa fa-check\"></i></button><div class=\"col-sm-3 col-xs-6\" style=margin-top:0.2em><small style=\"color:#ddd; font-size:70%\">press ENTER</small></div></div></div>");
|
||||||
$templateCache.put("../public/modules/forms/views/directiveViews/field/textfield.html",
|
$templateCache.put("../public/modules/forms/views/directiveViews/field/textfield.html",
|
||||||
"<div class=\"textfield field row\" ng-click=\"setActiveField(field._id, index, true)\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\" ng-style=\"{'color': design.colors.questionColor}\"><h3><span class=\"fa fa-angle-double-right\"></span> {{field.title}} <span class=required-error ng-show=\"field.required && !field.fieldValue\">*(required)</span></h3></div><div class=\"col-xs-12 field-input\"><input ng-style=\"{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}\" ng-focus=\"setActiveField(field._id, index, true)\" name={{field.fieldType}}{{index}} type={{field.input_type}} placeholder={{field.placeholder}} ng-class=\"{ 'no-border': !!field.fieldValue }\" class=\"focusOn text-field-input\" ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" value=field.fieldValue ng-required=field.required ng-disabled=field.disabled aria-describedby=inputError2Status on-enter-key=nextField()></div><div class=col-xs-12><div ng-show=forms.myForm.{{field.fieldType}}{{index}}.$invalid class=\"alert alert-danger\" role=alert><span class=\"glyphicon glyphicon-exclamation-sign\" aria-hidden=true></span> <span class=sr-only>Error:</span> Enter a valid email address</div></div></div><div class=\"col-xs-12 row\"><div class=\"btn btn-lg btn-default row-fluid\" style=\"padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)\"><button ng-disabled=\"!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid\" ng-style=\"{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}\" ng-click=$root.nextField() class=\"btn col-sm-5 col-xs-5\">OK <i class=\"fa fa-check\"></i></button><div class=\"col-sm-3 col-xs-6\" style=margin-top:0.2em><small style=\"color:#ddd; font-size:70%\">press ENTER</small></div></div></div>");
|
"<div class=\"textfield field row\" ng-click=\"setActiveField(field._id, index, true)\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\" ng-style=\"{'color': design.colors.questionColor}\"><h3><span class=\"fa fa-angle-double-right\"></span> {{field.title}} <span class=required-error ng-show=\"field.required && !field.fieldValue\">*(required)</span></h3></div><div class=\"col-xs-12 field-input\"><input ng-style=\"{'color': design.colors.answerColor, 'border-color': design.colors.answerColor}\" ng-focus=\"setActiveField(field._id, index, true)\" name={{field.fieldType}}{{index}} type={{field.input_type}} placeholder={{field.placeholder}} ng-class=\"{ 'no-border': !!field.fieldValue }\" class=\"focusOn text-field-input\" ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" value=field.fieldValue ng-required=field.required ng-disabled=field.disabled aria-describedby=inputError2Status on-enter-or-tab-key=nextField()></div><div class=col-xs-12><div ng-show=forms.myForm.{{field.fieldType}}{{index}}.$invalid class=\"alert alert-danger\" role=alert><span class=\"glyphicon glyphicon-exclamation-sign\" aria-hidden=true></span> <span class=sr-only>Error:</span> Enter a valid email address</div></div></div><div class=\"col-xs-12 row\"><div class=\"btn btn-lg btn-default row-fluid\" style=\"padding: 4px; margin-top:8px; background: rgba(255,255,255,0.5)\"><button ng-disabled=\"!field.fieldValue || forms.myForm.{{field.fieldType}}{{$index}}.$invalid\" ng-style=\"{'background-color':design.colors.buttonColor, 'color':design.colors.buttonTextColor}\" ng-click=$root.nextField() class=\"btn col-sm-5 col-xs-5\">OK <i class=\"fa fa-check\"></i></button><div class=\"col-sm-3 col-xs-6\" style=margin-top:0.2em><small style=\"color:#ddd; font-size:70%\">press ENTER</small></div></div></div>");
|
||||||
$templateCache.put("../public/modules/forms/views/directiveViews/field/yes_no.html",
|
$templateCache.put("../public/modules/forms/views/directiveViews/field/yes_no.html",
|
||||||
"<div class=\"field row radio\" ng-click=\"setActiveField(field._id, index, true)\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\"><h3 class=row><span class=\"fa fa-angle-double-right\"></span> {{field.title}} <span class=required-error ng-show=\"field.required && !field.fieldValue\">*(required)</span></h3><p class=row>{{field.description}}</p></div><div class=\"col-xs-12 field-input\"><div class=row><label class=\"btn btn-default col-md-2 col-sm-3 col-xs-4\" style=\"background: rgba(0,0,0,0.1); text-align:left\"><input type=radio value=true class=focusOn style=\"opacity: 0; margin-left: 0px\" ng-focus=\"setActiveField(field._id, index, true)\" ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" ng-required=field.required ng-click=$root.nextField() ng-disabled=\"field.disabled\"><div class=letter>Y</div><span>Yes</span> <i ng-show=\"field.fieldValue === 'true'\" class=\"fa fa-check\" aria-hidden=true></i></label></div><div class=row style=\"margin-top: 10px\"><label class=\"btn btn-default col-md-2 col-sm-3 col-xs-4\" style=\"background: rgba(0,0,0,0.1); text-align:left\"><input type=radio value=false style=\"opacity:0; margin-left:0px\" ng-focus=\"setActiveField(field._id, index, true)\" ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" ng-required=field.required ng-click=$root.nextField() ng-disabled=\"field.disabled\"><div class=letter>N</div><span>No</span> <i ng-show=\"field.fieldValue === 'false'\" class=\"fa fa-check\" aria-hidden=true></i></label></div></div></div><br>");
|
"<div class=\"field row radio\" ng-click=\"setActiveField(field._id, index, true)\"><div class=\"col-xs-12 field-title\" ng-style=\"{'color': design.colors.questionColor}\"><h3 class=row><span class=\"fa fa-angle-double-right\"></span> {{field.title}} <span class=required-error ng-show=\"field.required && !field.fieldValue\">*(required)</span></h3><p class=row>{{field.description}}</p></div><div class=\"col-xs-12 field-input\"><div class=row><label class=\"btn btn-default col-md-2 col-sm-3 col-xs-4\" style=\"background: rgba(0,0,0,0.1); text-align:left\"><input type=radio value=true class=focusOn style=\"opacity: 0; margin-left: 0px\" ng-focus=\"setActiveField(field._id, index, true)\" ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" ng-required=field.required ng-click=$root.nextField() ng-disabled=\"field.disabled\"><div class=letter>Y</div><span>Yes</span> <i ng-show=\"field.fieldValue === 'true'\" class=\"fa fa-check\" aria-hidden=true></i></label></div><div class=row style=\"margin-top: 10px\"><label class=\"btn btn-default col-md-2 col-sm-3 col-xs-4\" style=\"background: rgba(0,0,0,0.1); text-align:left\"><input type=radio value=false style=\"opacity:0; margin-left:0px\" ng-focus=\"setActiveField(field._id, index, true)\" ng-model=field.fieldValue ng-model-options=\"{ debounce: 250 }\" ng-required=field.required ng-click=$root.nextField() ng-disabled=\"field.disabled\"><div class=letter>N</div><span>No</span> <i ng-show=\"field.fieldValue === 'false'\" class=\"fa fa-check\" aria-hidden=true></i></label></div></div></div><br>");
|
||||||
$templateCache.put("../public/modules/forms/views/directiveViews/form/configure-form.client.view.html",
|
$templateCache.put("../public/modules/forms/views/directiveViews/form/configure-form.client.view.html",
|
||||||
|
|
|
@ -172,50 +172,51 @@ var questions = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log(chalk.green('\n\nHi, welcome to TellForm Setup'));
|
if(!fs.existsSync('./\.env')) {
|
||||||
|
console.log(chalk.green('\n\nHi, welcome to TellForm Setup'));
|
||||||
|
|
||||||
console.log(chalk.green('You should only run this the first time you setup TellForm\n--------------------------------------------------\n\n'));
|
console.log(chalk.green('You should only run this the first time you setup TellForm\n--------------------------------------------------\n\n'));
|
||||||
|
|
||||||
inquirer.prompt([questions[0]]).then(function (confirmAns) {
|
inquirer.prompt([questions[0]]).then(function (confirmAns) {
|
||||||
if(confirmAns['shouldContinue']) {
|
if (confirmAns['shouldContinue']) {
|
||||||
|
|
||||||
inquirer.prompt(questions.slice(1)).then(function (answers) {
|
inquirer.prompt(questions.slice(1)).then(function (answers) {
|
||||||
answers['NODE_ENV'] = 'production';
|
answers['NODE_ENV'] = 'production';
|
||||||
answers['SIGNUP_DISABLED'] = false ? answers['SIGNUP_DISABLED'] === false : true;
|
answers['SIGNUP_DISABLED'] = false ? answers['SIGNUP_DISABLED'] === false : true;
|
||||||
|
|
||||||
|
var email = answers['email'];
|
||||||
|
var pass = answers['password'];
|
||||||
|
delete answers['email'];
|
||||||
|
delete answers['password'];
|
||||||
|
|
||||||
var email = answers['email'];
|
envfile.stringify(answers, function (err, str) {
|
||||||
var pass = answers['password'];
|
fs.outputFile('./\.env', str, function (err) {
|
||||||
delete answers['email'];
|
if (err) return console.error(chalk.red(err));
|
||||||
delete answers['password'];
|
console.log(chalk.green('Successfully created .env file'));
|
||||||
|
});
|
||||||
|
user = new User({
|
||||||
|
firstName: 'Admin',
|
||||||
|
lastName: 'Account',
|
||||||
|
email: email,
|
||||||
|
username: email,
|
||||||
|
password: pass,
|
||||||
|
provider: 'local',
|
||||||
|
roles: ['admin', 'user']
|
||||||
|
});
|
||||||
|
|
||||||
envfile.stringify(answers, function (err, str) {
|
user.save(function (err) {
|
||||||
fs.outputFile('..//.env', str, function(err){
|
if (err) return console.error(chalk.red(err));
|
||||||
if (err) return console.error(chalk.red(err));
|
console.log(chalk.green('Successfully created user'));
|
||||||
console.log(chalk.green('Successfully created .env file'));
|
delete email;
|
||||||
});
|
delete pass;
|
||||||
user = new User({
|
|
||||||
firstName: 'Admin',
|
|
||||||
lastName: 'Account',
|
|
||||||
email: email,
|
|
||||||
username: email,
|
|
||||||
password: pass,
|
|
||||||
provider: 'local',
|
|
||||||
roles: ['admin', 'user']
|
|
||||||
});
|
|
||||||
|
|
||||||
user.save(function (err) {
|
console.log(chalk.green('Have fun using TellForm!'));
|
||||||
if (err) return console.error(chalk.red(err));
|
process.exit(1);
|
||||||
console.log(chalk.green('Successfully created user'));
|
});
|
||||||
delete email;
|
|
||||||
delete pass;
|
|
||||||
|
|
||||||
console.log(chalk.green('Have fun using TellForm!'));
|
|
||||||
process.exit(1);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
} else {
|
console.log(chalk.green('Have fun using TellForm!'));
|
||||||
console.log(chalk.green('Have fun using TellForm!'));
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ if (process.env.NODE_ENV === 'secure') {
|
||||||
console.log('--');
|
console.log('--');
|
||||||
|
|
||||||
process.on('uncaughtException', function (err) {
|
process.on('uncaughtException', function (err) {
|
||||||
console.error((new Date).toUTCString() + ' uncaughtException:', err.message);
|
console.error((new Date()).toUTCString() + ' uncaughtException:', err.message);
|
||||||
console.error(err.stack);
|
console.error(err.stack);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue